在进行代码审查时,我遇到了这个自定义钩子:
import { useRef, useEffect, useCallback } from 'react'
export default function useLastVersion (func) {
const ref = useRef()
useEffect(() => {
ref.current = func
}, [func])
return useCallback((...args) => {
return ref.current(...args)
}, [])
}
这个钩子是这样使用的:
const f = useLastVersion(() => { // do stuff and depends on props })
基本上,与 const f = useCallBack(() => { // do stuff }, [dep1, dep2])
相比,这避免了声明依赖项列表,并且 f
永远不会改变,即使其中一个依赖项发生变化。
我不知道如何看待这段代码。我不明白使用 useLastVersion
与 useCallback
相比有什么缺点。
答案 0 :(得分:0)
这个问题实际上已经或多或少地在文档中得到了回答:https://reactjs.org/docs/hooks-faq.html#how-to-read-an-often-changing-value-from-usecallback
有趣的部分是:
<块引用>另请注意,此模式可能会导致并发模式出现问题。我们计划在未来提供更多符合人体工程学的替代方案,但目前最安全的解决方案是,如果某个值取决于更改,则始终使回调无效。
也很有趣:https://github.com/facebook/react/issues/14099 和 https://github.com/reactjs/rfcs/issues/83
当前的建议是使用提供程序来避免在 props 中传递回调,如果我们担心会产生过多的重新渲染。
答案 1 :(得分:0)
我的观点如评论中所述,当依赖项更改过于频繁时(在 useEffect
/useCallback
dep数组),使用普通函数是最好的选择(没有开销)。
此钩子隐藏了使用它的组件的渲染,但渲染来自其父级中的 useEffect
。
如果我们总结一下我们得到的渲染计数:
Component
中渲染(由于value
)+在hook中渲染(useEffect
),总共2次。Component
中渲染(由于 value
)+ 在 Counter
中渲染(由于 value
更改导致函数引用更改),总共 2.< /li>
Component
中渲染 + 在 Counter
中渲染:每次渲染都有新函数,总共 2 个。但是您会在 useEffect
或 useCallback
中进行浅层比较的额外开销。
实际例子:
function App() {
const [value, setValue] = useState("");
return (
<div>
<input
value={value}
onChange={(e) => setValue(e.target.value)}
type="text"
/>
<Component value={value} />
</div>
);
}
function useLastVersion(func) {
const ref = useRef();
useEffect(() => {
ref.current = func;
console.log("useEffect called in ref+callback");
}, [func]);
return useCallback((...args) => {
return ref.current(...args);
}, []);
}
function Component({ value }) {
const f1 = useLastVersion(() => {
alert(value.length);
});
const f2 = useCallback(() => {
alert(value.length);
}, [value]);
const f3 = () => {
alert(value.length);
};
return (
<div>
Ref and useCallback:{" "}
<MemoCounter callBack={f1} msg="ref and useCallback" />
Callback only: <MemoCounter callBack={f2} msg="callback only" />
Normal: <MemoCounter callBack={f3} msg="normal" />
</div>
);
}
function Counter({ callBack, msg }) {
console.log(msg);
return <button onClick={callBack}>Click Me</button>;
}
const MemoCounter = React.memo(Counter);
附带说明,如果目的只是找到具有最少渲染次数的 input
的长度,阅读 inputRef.current.value
将是解决方案。