reactjs - 使用 useState “setter” 函数作为回调 ref 是否安全?
问题描述
将钩子的 setter 函数useState
用作回调 ref 函数是否安全?这会对 Suspense 或其他即将到来的 React 更改造成麻烦吗?如果“是的,这没关系”,那就太酷了!如果“不”为什么不呢?如果“也许”,那么什么时候可以,什么时候不行?
我问是因为我的一个组件需要安装三个 ref 才能调用 DOM API。其中两个必需的引用是通过 JSX 属性在同一组件中分配的“普通”引用ref
。稍后将通过 React 上下文将另一个 ref 分配到深度嵌套的组件中。我需要一种方法来在所有三个 ref 都安装后强制重新渲染父组件,并useEffect
在卸载任何 ref 时强制进行清理。
最初我编写了自己的回调 ref 处理程序,它调用了useState
我存储在上下文提供程序中的 setter。但后来我意识到,useState
setter 做了我自己的回调 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);
解决方案
每次出现目标元素时,您都会有额外的重新渲染(因为它可能会被有条件地渲染,它可能会发生不止一次)。
否则它应该工作得很好。除了,它可能会让未来的代码读者感到困惑。
我宁愿使用额外的功能 +useRef
来减少认知负担(又名“混乱”)。甚至更多:我们可能不需要回调 ref,因为我们有useEffect
:
const childRef = useRef();
useEffect(() => {
... do something
}, [childRef.current]);
<div ref={childRef} ...
但可以肯定的是,在非常普遍的情况下,这可能不适合我们,因为ref
每个目标元素的生命周期只调用两次回调,而useEffect
如果列表中有更多依赖项,则可能会更频繁地触发。但还是。
推荐阅读
- jquery - 使用 JQuery Raty on Rails 获取评分值
- android - Flutter:如何使用 ThemeData 设置“启用”TextField suffixIcon 颜色
- c# - 如何处理 EF Core 数据库迁移失败?
- python - 为什么我不能用 Python 3 获得这个 json 值?
- python - Python从文件夹的每个.txt文件中读取第5行
- mean - 如何计算子组的平均值
- leaflet - 更改 z-index 以在 Leaflet 地图上覆盖 div 时控件消失
- sorting - 根据插入前触发器中的日期字段对新记录进行排序
- oracle - 无法在 macOS Big Sur 上安装 DBD::Oracle => 未找到依赖的 dylib '@rpath/libclntsh.dylib.19.1'
- python - 无法从“ipykernel.pylab.backend_inline”导入名称“configure_inline_support”