Javascript 为什么不是';t反应';使用ref优化的回调?
TL;DR 为什么Javascript 为什么不是';t反应';使用ref优化的回调?,javascript,reactjs,react-hooks,usecallback,Javascript,Reactjs,React Hooks,Usecallback,TL;DR 为什么useCallback定义为(大致) 函数useCallback(回调,deps){ 返回useMemo((…参数)=>{ 返回回调(…args); },副署长); } 而不是像这样 函数useCallback(回调){ const fn=useRef(回调); fn.current=回调; 返回useMemo((…参数)=>{ 返回fn.当前值(…参数); }, []); } 它似乎可以解决不必要的重新加载,同时始终使用函数的最新版本。我还听说VUE3使用cacheHan
useCallback
定义为(大致)
函数useCallback(回调,deps){
返回useMemo((…参数)=>{
返回回调(…args);
},副署长);
}
而不是像这样
函数useCallback(回调){
const fn=useRef(回调);
fn.current=回调;
返回useMemo((…参数)=>{
返回fn.当前值(…参数);
}, []);
}
它似乎可以解决不必要的重新加载,同时始终使用函数的最新版本。我还听说VUE3使用cacheHandlers
选项进行优化
上下文解释版本 编写react组件/挂钩/上下文时,您可以直接编写函数:
const bootstrapAuth=async()=>{
// ...
};
…或使用useCallback
优化最小重新加载次数:
const bootstrapAuth=useCallback(异步()=>{
// ...
}, []);
我自己经常使用useCallback
,但作为一名教师,我从一开始就不教我的学生这一点。这是一个额外的并发症,而且是最严重的。我知道官方只考虑性能问题。只不过是usemo
,没别的了
但当您开始组合效果和功能时,它可能变得至关重要,例如:
const bootstrapAuth=async()=>{
// ...
};
useffect(()=>{
bootstrapAuth();
}, []);
^^这在技术上是不正确的(例如,linter会抱怨),但是,将bootstrapAuth
放在dependencies数组中更糟糕,因为它会在每次渲染时重新运行。似乎有三种解决方案:
使用useCallback
。但这违反了只是性能问题的原则,我不认为这在react社区中不是一个普遍的问题。我通常选择这种方法
bootstrapAuth
idempotent,也就是说,经常运行它不会产生额外的效果。使函数幂等始终是聪明的,但它似乎是一个奇怪的创可贴,因为它完全藐视效果挂钩。这肯定不是一般的解决办法
const bootstrapAuthLatest=useRef(bootstrapAuth);
bootstrapAuthLatest.current=bootstrapAuth;
useffect(()=>{
bootstrapAuthLatest.current();
}, []);
函数useCallback(回调){
const fn=useRef(回调);
fn.current=回调;
返回useMemo((…参数)=>{
返回fn.当前值(…参数);
}, []);
}
我自己,我倾向于在任何地方使用useCallback,[…]
与
const bootstrapAuth = async () => {
// ...
};
const bootstrapAuthCallBack = useCallback(bootstrapAuth, [])
您有效地创建了一个附加函数(与不使用
useCallback
相比),一个数组,分配了相应的内存,并让useCallback
设置其属性并运行逻辑表达式。在useCallback
中调用所有内容会使所有代码在默认情况下运行得更快。会回答您的问题吗?我不是很确定,但它确实触及了问题的核心。Dan经常喜欢强调该方法的“语义正确性”,以及它如何消除bug等。我的问题不是关于正确性,而是关于正确性与以下两者之间的紧张关系:优化重播(如有必要)和/或对初始道具值的单次出现效应。Dan基本上会说:“我们实现了useCallback
是语义上最正确的方法,以避免出现bug。如果可能的话,不要“提前到达”未来的道具。“我的问题是,这看起来非常尴尬。如果你想在初始道具值上运行一次效果,那么从语义上来说,你必须首先用refs编码“那些初始值”,然后在useffect(…,[])中编写你的函数
。那么,这可能是Dan首选的解决方案。至于重新渲染器的优化,hooks api非常不鼓励编写“类似“对实例的最新状态/道具进行操作的回调函数,这反过来又使rerender优化变得更加困难。当然,Dan说现在面临的更困难的任务是语义正确性的任务是正确的。Dan的这句话在我看来切中要害:“从概念上讲,你可以想象效果是渲染结果的一部分。”(我的编辑:effects and callbacks)我知道这一点。使用useCallback
包装函数的原因是为了在将其作为道具传递给子组件时保持引用稳定性,以便可以优化子组件以排除不必要的重新加载,例如使用React.memo
或componentShouldUpdate
。问题不在于为什么/是否/何时这样做,而在于为什么useCallback
是以这种方式实现的,以及为什么没有选择“ref版本”。那么,不要说你把它放在任何地方,而实际上你只在它有用的时候才应用它。好吧,好吧,当然,我会编辑:)我将“where”改为“frequency”。(事实是,我现在正在开发一个React原生应用程序,然后重新发布优化变得非常重要,所以我实际上的意思是“无论在哪里”。对于同一个应用程序的web应用程序版本,我几乎从不使用它。)
const bootstrapAuth = async () => {
// ...
};
const bootstrapAuthCallBack = useCallback(bootstrapAuth, [])