今天使用 React 的 ref 可能会有点令人困惑。 回到类组件的时代,在文档中非常清楚。
我们应该主要对DOM 元素使用 refs:
https://reactjs.org/docs/refs-and-the-dom.html
但是今天我们有钩子和功能组件在一起。 我们正在使用useRef
钩子。
此外,这为我们带来了新的模式。 使用 ref 包含回调或我们想要保留的任何数据(状态类型(,但无需重新渲染。 这是一个功能强大的API,也显示在文档中:
https://reactjs.org/docs/hooks-faq.html#is-there-something-like-instance-variables
所以现在 ref 可以用于:
-
存储可变数据
-
记忆的种类
但是这些文档相互矛盾。 并在开发团队中造成许多错误和冲突。
文档说了两件不同的事情,这是一个问题。
那么,在这种情况下,"正确"的做法是什么?
const MyComponent = (props) => {
const [myMap1, _] = useState(new Map()); // 1.
const myMap2 = useMemo(()=> new Map(), []); // 2.
const myMap3 = useRef(new Map()); // 3.
...
};
useMemo
是一种声明性性能优化技术。
声明式,因为它依赖于属于特定渲染的周围范围的状态或 props 依赖项,并自动(重新(创建记忆值。我们不必告诉useMemo
这样做。
useRef
是任何值的可变存储框,例如独立于当前渲染范围的值更新。
useRef
没有任何依赖项,因此不会自动更改任何值。您必须通过编写ref.current = ...
手动执行此操作(DOM 节点是一个例外(。 这就是为什么你可以把useRef
看作是进入命令式世界的逃生舱口。
// 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
甚至用useState
替换useRef
:const [ref] = useState({current:null});
ref.current = "hello";
缺点:我们又回到了旧的jQuery时代:-(。操作必须势在必行,这意味着更多的手动工作和可能的错误。不可变值操作也比突变更可预测(包括其他好处(。也就是说,为什么 React 中的几乎所有内容都是不可变的。
相反,尽可能保持声明性(React 惯用语(并针对其特定情况使用预期的优化工具是有意义的:
- 以声明方式
useState
不可变值,触发器重新呈现 useMemo
用于声明性记忆从状态和道具派生的昂贵值useRef
可变值或 DOM 节点 - 独立于渲染闭包范围,无需deps
,不会触发重新渲染,更多的手动命令式工作
钩子 useRef 可用于存储任何值,例如,您有一个对象、数组或映射,您不想在每次重新渲染时重新初始化它们。 你可以使用useRef hook。
// 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 很有用(当将 props 作为对象传递或将记忆对象传递给传递给上下文提供程序的值 prop 时很有用(。