Ref objects inside useEffect Hooks
原文: Ref objects inside useEffect Hooks
当我们要实现一个自定义 hook, 索引到某个 DOM 节点的时候, 你或许首先会想到使用 useRef
hook 来实现. 这也是我的首选项.
因此在我初次实现 react-intersection-observer 的时候, 就使用了这种实现方式. 但是在实现过程中, 发现了一个很有趣的问题: 如何处理 hooks 的条件渲染?.
最主要的问题之一是, 我以为 ref
始终会有值, 但是实际情况是:
ref
的初始值是undefined
, 之后才会被赋值.ref
有可能变为另一个完全不同的元素.ref
有可能从一个元素变为undefined
.
我的自定义 hook 并没有处理以上任何一种情况.
ref 和 useEffect 的问题
useRef
hook 对我们的自定义 hook 来说是一个陷阱, 如果搭配 useEffect
一起使用的话. 你的直觉应该是把 ref.current
加到 useEffect
的依赖参数中, 这样一来, 一旦 ref
变化, useEffect
中的函数也会重新执行.
但问题来了, 只有当组件重新渲染的时候, ref
值才会有所变化, 因此在 useEffect
中, 在下一次渲染完成之前, 都不会捕获到 ref
值的变化.
观察社区中的这些自定义 hooks, 会发现这是一个普遍存在的问题. 一些自定义 hooks 的网站, 比如useHooks, the-platform 都跌入了这个陷阱中, 因为大部分开发者都认为这个 ref
, 在第一次渲染之后, 就不会变化了.
于是我在 Twitter 上直接去问了 Dan Abramov 如何解决这个问题.
React 团队确实会给我们以警示, 当我们跳过某些副作用的执行的时候.
如何解决
在这种情况下, 我们不应该使用 useRef
, 而是用 useCallback
来创建一个回调 ref(callback ref).
1 | function useHookWithRefCallback() { |
一旦某个 DOM 节点触发了这个回调函数, 你就能够根据需要消费对应的 DOM 节点.
这里有个比较完整的示例, 可以观察一下 react-intersection-observer 内部所依赖的 useInView 的源码
官方的解决方案
React 团队在文档的 FAQ 中也有一个相关的例子, 使用 ref callback 来计算 DOM 元素的大小尺寸等. 对于我们所提出的问题, 这也是一个解决方案.