lmk123 / blog

个人技术博客,博文写在 Issues 里。
https://github.com/lmk123/blog/issues
623 stars 35 forks source link

升级到 React 18 后的第一个 bug 出现了 #117

Open lmk123 opened 1 year ago

lmk123 commented 1 year ago

前段时间,我发布了划词翻译 v9.0.0,这个版本其实就只是把所有依赖项升级到了最新版本,其中就包括把 React 17 升级到了 React 18。升级完成后我大致测了一下,没有问题,但今天突然发现:划词翻译的翻译窗口不能拖动了。

翻译窗口的拖动功能大致可以简化成以下代码:

function App() {
  // 组件的其它状态忽略掉…

  const containerRef = useRef(null)

  useAddDrag(containerRef.current)

  return <div ref={containerRef}>翻译结果</div>
}

在写这个代码的时候,我很清楚,当 containerEleRef.currentnull 变为具体的 div 元素时,是不会引起组件重新渲染的,但我认为组件内还有很多其它状态会引起组件重新渲染,所以 useAddDrag(containerEleRef.current) 一定会在 containerEleRef.current 有值时运行一次,而事实证明这段代码在使用 React 17 时一直没出过问题。

然而,在升到 React 18 之后就有了这个问题,而且奇怪的是,本地开发时是不会出现这个问题的,只有将代码以生产环境模式打包之后才会出现这个问题。

当代码以开发模式运行时,useAddDrag(containerEleRef.current) 会运行两次,第一次时 containerEleRef.currentnull,第二次则是具体的 div 元素,这是符合我一直以来的预期的。

但是当代码以生产环境模式打包后,useAddDrag(containerEleRef.current) 只会运行一次,且第一次时 containerEleRef.currentnull。而且,在 Chrome 版本 107.0.5304.110(正式版本) (arm64) 里偶尔会正常运行第二次,但在 Firefox 里则一定只会运行一次,我是搞不懂了 :joy:

将以上代码改为下面这种形式后,问题就解决了:

function App() {
  // 组件的其它状态忽略掉…

  const [container, setContainer] = useState(null)

  useAddDrag(container)

  return <div ref={setContainer}>翻译结果</div>
}