ampproject / ampproject/amp-react-prototype
A safe useEffect for refs
- 主要语言
- JavaScript
- 星标
- 36
- 派生
- 6
- PR 合并指标
- 30 天内没有已合并 PR
描述
The pattern:
```
const ref = useRef();
useEffect(() => {
ref.current.addEventListener(...);
return () => ref.current.removeEventListener(...);
}, [...])
return (
{props.mode === 1 ? : }
);
```
In this pattern, it's hard to react safely to changes of `ref.current`, e.g. when a node mapped to it is deleted/changes. The "right" way to do this is to ensure that the `deps` array contains the same condition that affects `ref`. E.g. `[props.mode]` in the example above. However, it's not always obvious and easy to miss.
Some solutions are below.
/1/ Ask nicely for `deps` to be correct and hope for the best
Hopefully an "exhaustive deps" linter would not remove the extra dep.
/2/ Ban changing of Ref mapping.
I.e. disallow the example above. This could be hard with `forwardRef`.
/3/ Use state function instead of ref:
```
const [node, setNode] = useState();
useEffect(() => {...}, [node])
return <...>
```
The negative: it forces the second rerender each time the ref changes.
/4/ Use a funky `xEffectWithRef` version.
It'd manage the `ref` value internally and could look something like this:
```
function useEffectWithRef(ref, effect, deps) {
const unsubscribe = useRef(null);
const prev = useRef(null);
useEffect(() => {
return () => doUnsubscribe(prev, unsubscribe);
}, deps || []);
useEffect(() => {
const {current} = ref;
if (current !== prev.current) {
doUnsubscribe(prev, unsubscribe);
prev.current = current;
if (current) {
unsubscribe.current = effect(current);
}
}
});
}
```
The positive: it doesn't cause rerender.
A negative: one effect is executed each time, but it will almost always do nothing.
贡献指南
调研方向
从 issue 中展示的 useEffect 和 ref 模式开始,包括基于状态的替代方案和 useEffectWithRef 替代方案。比较它们对 ref 变更和清理的处理方式;issue 没有指定目标文件、测试或已达成一致的实现,因此未规定完成定义。
由索引模型根据 Issue 内容生成。
评估
- 技术栈
- javascript, react
- 领域
- frontend
- Issue 类型
- 功能
- 难度
- 5/5
- 预计耗时
- 一周以上
- 活跃度
- 停滞
- 描述清晰度
- 需要澄清
- 新手友好度
- 25/100