引用DOM元素的React Hook在首次调​​用时返回“ null”

时间:2019-10-05 12:32:37

标签: javascript reactjs react-hooks

我有一个钩子,钩住了ref DOM元素的getBoundingClientRect对象。问题是,在第一次渲染时,它返回null,我只需要在组件的第一次渲染时获取值。

我在功能组件中像这样使用它:

const App = () => {

  // create ref 
  const rootRef = useRef(null);

  // get Client Rect of rootRef 
  const refRect = useBoundingClientRect(rootRef);

  useEffect(()=> {
    // return "null" the first time
    // return "DOMRect" when refRect is update
    console.log(refRect)
  }, [refRect])

  return <div ref={rootRef} >App</div>

}

在这里,我在应用程序组件中调用useBoundingClientRect钩子。

export function useBoundingClientRect(pRef) {

  const getBoundingClientRect = useCallback(() => {
    return pRef && pRef.current && pRef.current.getBoundingClientRect();
  }, [pRef]);

  const [rect, setRect] = useState(null);

  useEffect(() => {
    setRect(getBoundingClientRect());
  },[]);

  return rect;
}

问题是我想在init上缓存boundingClientRect对象,而不是第二次重新呈现组件:

  // App Component

  useEffect(()=> {
    // I would like to get boundingClientRect the 1st time useEffect is call.
    console.log(refRect)

  // empty array allow to not re-execute the code in this useEffect
  }, [])

我查看了一些教程和文档,发现有些人使用useRef而不是useState挂钩来保持价值。因此,我尝试在useboundingClientRect挂钩中使用它,以在App组件的第一个渲染中捕获并返回boundingClientRect值。它的工作原理是……

export function useBoundingClientRect(pRef) {

  const getBoundingClientRect = useCallback(() => {
    return pRef && pRef.current && pRef.current.getBoundingClientRect();
  }, [pRef]);

  const [rect, setRect] = useState(null);

  // create a new ref
  const rectRef = useRef(null)

  useEffect(() => {
    setRect(getBoundingClientRect());

    // set value in ref
    const rectRef = getBoundingClientRect()

  },[]);

  //  return rectRef for the first time 
  return rect === null ? rectRef : rect;
}

现在,应用程序组件中的console.log(rectRef)允许访问第一个渲染的值:

   // App Component

  useEffect(()=> {
    console.log(refRect.current)
  }, [])

但是,如果我尝试从refRect.current钩返回useBoundingClientRect,请返回null。 (什么?!)

如果有人可以向我解释这些错误。预先感谢!

1 个答案:

答案 0 :(得分:1)

您需要在此处了解更新的参考,变异和异步性质

首先,当您使用状态存储clientRect属性时,当您运行自定义钩子useEffect时,由于状态更新是异步的,因此它将状态设置为值,该值将反映在下一个渲染周期中。这就是为什么在第一次渲染时会看到未定义的原因。

第二,当您返回rectRef时,实际上是在返回一个对象,该对象随后在useEffect中的useBoundingClientRect运行时发生变异。在渲染周期之后运行useEffect之前,将返回数据。现在,当组件中的useEffect(在自定义钩子中的useEffect之后)运行时,数据已经存在,并且已被先前的useEffect对其进行了更新,因此您将看到正确的数据。 / p>

但是在这里,如果您返回rectRef.current现在是一个不可变的值,则自定义钩子会更新该值,但是由于前一个是null,因此该钩子将使用新的引用,因此您看不到更改在您的组件useEFfect方法中。