使用 useState“setter”函数作为回调引用是否安全?

Jus*_*ant 11 reactjs react-context react-hooks react-suspense

useState钩子的 setter 函数用作回调 ref 函数是否安全?这是否会导致 Suspense 或其他即将到来的 React 更改出现问题?如果“是的,这没问题”,那就太酷了!如果“不”为什么不呢?如果“可能”,那么什么时候可以,什么时候不行?

我这么问是因为我的一个组件在调用 DOM API 之前需要挂载三个 refs。其中两个必需的 ref 是通过 JSXref道具在同一组件中分配的“普通” ref 。另一个 ref 将在稍后的某个时间通过 React 上下文分配到一个深层嵌套的组件中。我需要一种方法来在安装所有三个引用后强制重新渲染父组件,并useEffect在卸载任何引用时强制进行清理。

最初我编写了自己的回调引用处理程序,它调用了useState我存储在上下文提供程序中的setter。但后来我意识到useStatesetter 做了我自己的回调 ref 所做的一切。仅使用 setter 而不是编写自己的回调 ref 函数是否安全?或者有没有更好和/或更安全的方法来做我想做的事情?

我尝试谷歌搜索"useState" "callback ref"(和其他类似的关键字变体)但结果没有帮助,除了@theKashey的优秀use-callback-ref包我肯定会在其他地方使用(例如,当我需要将回调引用传递给组件时)需要一个 RefObject,或者当我需要一个回调并在本地使用一个 ref 时)但在这种情况下,所有回调需要做的就是在 ref 更改时设置一个状态变量,所以 Anton 的包在这里似乎有点过分了。

下面和https://codesandbox.io/s/dreamy-shockley-5dc74 有一个简化的例子。

import * as React from 'react';
import { useState, forwardRef, useEffect, createContext, useContext, useMemo } from 'react';
import { render } from 'react-dom';

const Child = forwardRef((props, ref) => {
  return <div ref={ref}>This is a regular child component</div>;
});

const refContext = createContext();
const ContextUsingChild = props => {
  const { setValue } = useContext(refContext);
  return <div ref={setValue}>This child uses context</div>;
};

const Parent = () => {
  const [child1, setChild1] = useState(null);
  const [child2, setChild2] = useState(null);
  const [child3, setChild3] = useState(null);

  useEffect(() => {
    if (child1 && child2) {
      console.log(`Child 1 text: ${child1.innerText}`);
      console.log(`Child 2 text: ${child2.innerText}`);
      console.log(`Child 3 text: ${child3.innerText}`);
    } else {
      console.log(`Child 1: ${child1 ? '' : 'not '}mounted`);
      console.log(`Child 2: ${child2 ? '' : 'not '}mounted`);
      console.log(`Child 3: ${child3 ? '' : 'not '}mounted`);
      console.log(`In a real app, would run a cleanup function here`);
    }
  }, [child1, child2, child3]);

  const value = useMemo(() => ({ setValue: setChild3 }), []);

  return (
    <refContext.Provider value={value}>
      <div className="App">
        This is text in the parent component
        <Child ref={setChild1} />
        <Child ref={setChild2} />
        <ContextUsingChild />
      </div>
    </refContext.Provider>
  );
};

const rootElement = document.getElementById('root');
render(<Parent />, rootElement);
Run Code Online (Sandbox Code Playgroud)

小智 0

useState“setter”函数在渲染周期中保持相同的引用,因此这些函数应该可以安全使用。甚至提到它们可以在依赖数组上省略:

\n
\n

(setCount 函数的身份保证是稳定的,因此它\xe2\x80\x99s 可以安全地省略。)

\n
\n

您可以直接传递 setter 函数:

\n
<refContext.Provider value={setChild3}>\n
Run Code Online (Sandbox Code Playgroud)\n

...然后阅读:

\n
const ContextUsingChild = props => {\n  const setChild3 = useContext(refContext);\n  return <div ref={setChild3}>This child uses context</div>;\n};\n
Run Code Online (Sandbox Code Playgroud)\n