我应该使用 useRef 来存储一次性初始化数据吗?

Should I use useRef for storing one-time initialised data?

今天使用 React 的 ref 可能有点混乱。 回到 class 组件的时代,在文档中非常清楚。

我们应该主要对 DOM 元素使用 refs :

https://reactjs.org/docs/refs-and-the-dom.html

但是今天我们有钩子和功能组件。 我们正在使用 useRef 钩子。

另外,这给我们带来了新的模式。使用 ref 包含 callbacks,或任何我们想要保留(某种状态)但不需要重新渲染的 data。 它功能强大 API 并且还出现在文档中:

https://reactjs.org/docs/hooks-faq.html#is-there-something-like-instance-variables

所以现在 ref 可以用于:

  1. 存储可变数据

  2. 一种记忆

但这些文档相互矛盾。并在开发团队中造成许多错误和冲突。

医生说了 2 个不同的事情,这是一个问题。

那么,在这种情况下 "right" 该怎么办?


const MyComponent = (props) => {
   const [myMap1, _] = useState(new Map());    // 1.
   const myMap2 = useMemo(()=> new Map(), []); // 2.
   const myMap3 = useRef(new Map());           // 3.

   ...
};

挂钩 useRef 可用于存储任何值,例如,您有一个对象或数组或地图,您不想在每次重新渲染时都重新初始化 你可以使用 useRef 钩子。

 // this code will recreate the object in memory on each rerender
 const myObj = {foo: 'foo', bar: 'bar'}

 // here you have the same object in memory even after rerenders
 const refObj = useRef({foo: 'foo', bar: 'bar'})

同样,useState hook可以用来存储普通的状态变量。

当某些值依赖于代码中的另一个变量并且您只想在变量发生变化时更改值时useMemo很有用(在将道具作为对象传递或将记忆对象传递给传递给值的道具时很有用上下文提供者)。

useMemo 是一种 声明式 performance optimization 技术。

声明式,因为它依赖于属于特定渲染的周围范围的状态或道具依赖性,并自动(重新)创建记忆值。我们不必告诉 useMemo 这样做。

useRef 是一个 mutable 存储框,可以存储任何值,例如独立于当前渲染范围的值更新。

useRef 没有任何依赖关系,因此不会自动更改任何值。您必须通过编写 ref.current = ... 手动执行此操作(DOM 节点除外)。 这就是为什么您可以将 useRef 视为进入 势在必行的 世界的逃生口。

现在,我们可以尝试混合它们的 usage interchangeably 进行存储,例如:
// instead of 
const val = useMemo(() => 42, [myDep]); // 42 stands for some complex calculation
// do this
const ref = useRef();
ref.current = 42 // assign 42 imperatively to ref somewhere in render
甚至 replace useRef useState
const [ref] = useState({current:null});
ref.current = "hello";

缺点:我们又回到了旧 jQuery 年龄 :-)。必须强制设置操作,这意味着更多的手动工作和可能的错误。不可变值操作也比突变更可预测(包括其他好处)。这就是为什么 React 中几乎所有的东西都是不可变的。

相反,尽可能保持声明式(React 惯用语)并针对特定情况使用预期的优化工具是有意义的:

  • useState 对于声明方式的不可变值,触发重新渲染
  • useMemo 用于昂贵值的声明性记忆 派生自 state 和 props
  • useRef for mutable values or DOM nodes - 独立于渲染闭包范围,不需要 deps,不触发重新渲染,更多手动必要工作