在React+Typescript中编写组件时,我经常想在其中一个道具上触发useEffect钩子。例如,考虑一个按钮,它为每次连续的鼠标点击执行不同的功能:
export function ActionButton({clickActions}: {clickActions: (() => void)[]}) {
const [clicked, setClicked] = useState(0);
useEffect(() => {
if (clickActions.length > 0 && clickActions[clicked % clickActions.length]) {
clickActions[clicked % clickActions.length]();
}
}, [clicked, clickActions])
return <button onClick={() => setClicked(clicked => clicked + 1)}/>
}
在这种情况下,该组件的客户端需要意识到,他们需要以某种方式防止clickActions
在每次渲染中成为不同的实例。例如,它可以简单地是一个常数,也可以使用useMemo
进行记忆。
有没有让我的客户意识到这一点的最佳实践?当违反此规则时,是否有方法触发编译时错误?
注意:我意识到这个特殊情况可以在不使用useEffect
的情况下解决,但这只是一个简单的示例来说明模式。我对解决这个特定问题不感兴趣,而是对如何解决一般问题感兴趣。
您无法通过TypeScript技巧强制执行clickActions
不能在每次父级渲染时更改。
但是,您可以简单地从useEffect
的依赖项列表中删除clickActions
。通常,在执行此操作时必须小心,但在这种情况下,这是安全的,因为当clicked
发生更改时,同步传递给useEffect
的回调将执行一个操作,这意味着回调将在需要时引用最新的clickActions
。
这类似于RxJS中的sample
运算符;即CCD_ 11由CCD_。
useEffect(() => {
if (clickActions.length > 0 && clickActions[clicked % clickActions.length]) {
clickActions[clicked]();
}
}, [clicked])
还请注意,即使父级使用useMemo
,也不能保证在React选择时不会重新创建clickActions
您可以将
useMemo
作为性能优化,而不是语义保证将来,React可能会选择"忘记"一些以前存储的值,并在下次渲染时重新计算它们,例如为屏幕外组件释放内存。编写代码,使其在没有useMemo
的情况下仍然可以工作,然后添加它以优化性能。
(请参阅useMemo文档(
可以肯定地假设useCallback
也有类似的警告,尽管文档中没有特别提到。
通常情况下,如果您有一个函数正在传递其重要性(特别是如果它在函数中"created"(,则在创建它时使用useCallback
;道具不变"从而防止重新应答。
parentFunction = useCallback(()=>{
if (specialVar==='dance'){
return () => {
console.log('dance')
}
}
else {
return () => {
console.log('do the boogy')
}
}
}, [specialVar])