我知道 ref 是一个可变容器,因此不应在useEffect的依赖项中列出它,但它ref.current可能是一个不断变化的值。
当 ref 用于存储像 DOM 元素时<div ref={ref}>,并且当我开发依赖于该元素的自定义钩子时,假设ref.current如果组件有条件地返回,则可以随着时间的推移而改变,例如:
const Foo = ({inline}) => {
const ref = useRef(null);
return inline ? <span ref={ref} /> : <div ref={ref} />;
};
Run Code Online (Sandbox Code Playgroud)
我的自定义效果接收ref对象并ref.current用作依赖项是否安全?
const Foo = ({inline}) => {
const ref = useRef(null);
return inline ? <span ref={ref} /> : <div ref={ref} />;
};
Run Code Online (Sandbox Code Playgroud)
我读过这个评论说 ref 应该用于useEffect,但我无法弄清楚任何情况下ref.current改变但效果不会触发。
正如那个问题所建议的那样,我应该使用回调引用,但是引用作为参数对于集成多个钩子非常友好:
const useFoo = ref => {
useEffect(
() => {
const element = ref.current;
// Maybe observe the resize of element
},
[ref.current]
);
};
Run Code Online (Sandbox Code Playgroud)
虽然回调引用更难使用,因为强制用户组合它们:
const fooRef = useFoo();
const barRef = useBar();
const ref = element => {
fooRef(element);
barRef(element);
};
<div ref={ref} />
Run Code Online (Sandbox Code Playgroud)
这就是为什么我要问ref.current在useEffect.
Den*_*ash 36
因为变异的参考它没有安全不会引发渲染,因此,不会触发的useEffect。
React Hook useEffect 有一个不必要的依赖:'ref.current'。排除它或删除依赖项数组。像 'ref.current' 这样的可变值不是有效的依赖项,因为改变它们不会重新渲染组件。(反应钩子/详尽的deps)
反模式示例:
const Foo = () => {
const [, render] = useReducer(p => !p, false);
const ref = useRef(0);
const onClickRender = () => {
ref.current += 1;
render();
};
const onClickNoRender = () => {
ref.current += 1;
};
useEffect(() => {
console.log('ref changed');
}, [ref.current]);
return (
<>
<button onClick={onClickRender}>Render</button>
<button onClick={onClickNoRender}>No Render</button>
</>
);
};
Run Code Online (Sandbox Code Playgroud)
一个现实生活中的使用情况下,与此相关的模式是,当我们希望有一个持续性的参考,该元素卸除甚至当。
检查下一个示例,其中我们无法在卸载时坚持调整元素大小。我们将尝试useRef与useEffect上述组合一起使用,但它不起作用。
// BAD EXAMPLE, SEE SOLUTION BELOW
const Component = () => {
const ref = useRef();
const [isMounted, toggle] = useReducer((p) => !p, true);
const [elementRect, setElementRect] = useState();
useEffect(() => {
console.log(ref.current);
setElementRect(ref.current?.getBoundingClientRect());
}, [ref.current]);
return (
<>
{isMounted && <div ref={ref}>Example</div>}
<button onClick={toggle}>Toggle</button>
<pre>{JSON.stringify(elementRect, null, 2)}</pre>
</>
);
};
Run Code Online (Sandbox Code Playgroud)
令人惊讶的是,为了修复它,我们需要node在记忆函数时直接处理useCallback:
// GOOD EXAMPLE
const Component = () => {
const [isMounted, toggle] = useReducer((p) => !p, true);
const [elementRect, setElementRect] = useState();
const handleRect = useCallback((node) => {
setElementRect(node?.getBoundingClientRect());
}, []);
return (
<>
{isMounted && <div ref={handleRect}>Example</div>}
<button onClick={toggle}>Toggle</button>
<pre>{JSON.stringify(elementRect, null, 2)}</pre>
</>
);
};
Run Code Online (Sandbox Code Playgroud)
Gyu*_*Fox 11
2021 答案:
本文介绍的问题用参连同useEffect:参考useEffect钩中的对象:
如果将 useRef 钩子与跳过渲染的 useEffect 结合使用,则它可能成为自定义钩子的陷阱。您的第一直觉是将 ref.current 添加到 useEffect 的第二个参数,因此一旦 ref 更改它就会更新。但是 ref 直到您的组件渲染后才会更新——这意味着,任何跳过渲染的 useEffect 在下一次渲染之前都不会看到对 ref 的任何更改。
同样如本文所述,官方 react 文档现已更新为推荐方法(即使用回调而不是 ref + 效果)。请参阅如何测量 DOM 节点?:
function MeasureExample() {
const [height, setHeight] = useState(0);
const measuredRef = useCallback(node => {
if (node !== null) {
setHeight(node.getBoundingClientRect().height);
}
}, []);
return (
<>
<h1 ref={measuredRef}>Hello, world</h1>
<h2>The above header is {Math.round(height)}px tall</h2>
</>
);
}
Run Code Online (Sandbox Code Playgroud)
我遇到了同样的问题,我使用 Typescript 创建了一个自定义钩子,并使用 ref 回调创建了一个官方方法。希望它会有所帮助。
export const useRefHeightMeasure = <T extends HTMLElement>() => {
const [height, setHeight] = useState(0)
const refCallback = useCallback((node: T) => {
if (node !== null) {
setHeight(node.getBoundingClientRect().height)
}
}, [])
return { height, refCallback }
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
19501 次 |
| 最近记录: |