文章摘要
文章探讨了在React中使用useCallback和useMemo进行性能优化的必要性,指出在许多情况下这些钩子并无实际作用,反而可能增加代码复杂性。作者强调,只有在确实需要减少不必要的重新渲染或计算时,才应使用这些钩子,否则应避免滥用。
文章总结
标题:无用的useCallback
主要内容:
作者在文章中探讨了React中的useCallback和useMemo在某些情况下的无效使用。尽管作者之前已经写过关于记忆化(memoization)的文章,但他发现最近有一种模式频繁出现,让他觉得有必要再次讨论这个话题。
为什么需要记忆化?
通常,使用useCallback或useMemo进行记忆化的原因只有两个:
性能优化:某些操作较慢,理想情况下我们希望加快速度,但有时无法做到,因此我们尝试减少这些慢操作的执行频率。在React中,慢操作通常是子树的重新渲染,因此我们希望避免不必要的渲染。
防止副作用频繁触发:如果记忆化的值最终作为依赖项传递给副作用(effect),我们需要确保这些依赖项的引用是稳定的,否则副作用可能会在每次渲染时重新运行。
无用的记忆化场景
没有记忆化组件,就没有性能提升:如果记忆化的值或函数没有传递给记忆化组件,那么记忆化是无效的。例如,将
onClick传递给普通的button组件时,记忆化onClick没有任何意义,因为button并不关心onClick的引用是否稳定。使用props作为依赖项:将非原始类型的props作为内部依赖项添加到依赖数组中通常是不正确的,因为组件无法控制这些props的引用稳定性。例如,如果
onChange是一个内联函数,那么记忆化handleChange是无效的。
真实案例
作者以Sentry代码库中的useHotkeys自定义钩子为例,展示了记忆化在实际应用中的问题。尽管大多数使用useHotkeys的地方都对输入进行了记忆化,但如果深入分析,会发现记忆化仍然可能失效。例如,paginateHotkeys依赖于paginateItems,而paginateItems又依赖于attachments.filter,后者每次都会生成一个新的数组,导致所有下游的记忆化都失效。
解决方案
最新引用模式:通过使用
useRef来存储需要访问的值,并在每次渲染时更新它,从而避免依赖数组中的引用问题。useEffectEvent:React即将引入
useEffectEvent,这是一个专门用于在副作用中访问最新值的原语,可以避免不必要的useCallback或useMemo。
结论
作者认为,记忆化在实际应用中经常失效,增加了代码的复杂性和维护成本。他建议尽量避免不必要的记忆化,并期待React未来提供的useEffectEvent能够简化这一过程。
互动
读者可以通过Bluesky或评论区与作者交流。
评论总结
评论内容主要围绕React的Hooks、memoization(如useMemo和useCallback)以及React的设计复杂性展开,观点多样且存在争议。以下是总结:
1. Hooks的复杂性与问题
- 观点:Hooks带来了复杂性和潜在的错误,尤其是在依赖管理和异步更新时。
- 论据:
useEffect的依赖数组强制声明所有依赖,导致不必要的重新渲染(matsemann)。- 异步更新时,
useState的回调机制难以处理逻辑耦合(matsemann)。 - Hooks的设计使得代码变得难以维护,甚至“像意大利面条”(matsemann)。
2. memoization的争议
- 支持memoization:
- 观点:
useMemo和useCallback有助于避免不必要的重新渲染,尤其是在处理非原始类型时。 - 论据:
- 确保引用相等性(referential equality)是必要的,尤其是在大型应用中(strogonoff)。
- 即使在小应用中,memoization也不会带来显著的性能损失(zeorin)。
- 观点:
- 反对memoization:
- 观点:memoization增加了代码复杂性,且容易出错。
- 论据:
useMemo和useCallback的使用常常是多余的,尤其是在小型应用中(wfleming)。- 如果父组件没有正确memoize,子组件的优化也会失效(prinny_)。
3. React的设计问题
- 观点:React的设计模式(如“倒置的响应式模型”)增加了复杂性,且不如其他框架直观。
- 论据:
- React要求开发者显式地“选择退出”状态更新,而其他框架(如Vue、Svelte)则更自然地处理状态变化(CharlieDigital)。
- React的复杂性源于其设计,而非开发者的问题(CharlieDigital)。
4. React的未来与替代方案
- 观点:React的复杂性可能为其他框架铺平道路。
- 论据:
- React的新特性(如
use、useForm)更像是“创可贴”式的修复,而非真正的创新(prinny_)。 - 一些开发者开始探索替代方案,如无Hooks的设计模式(patrickthebold)。
- React的新特性(如
5. React.memo的默认使用
- 观点:
React.memo应默认启用,以减少不必要的重新渲染。 - 论据:
- 递归重新渲染的成本可能高于默认memoization的成本(SSchick)。
- 引用相等性检查的成本较低,且默认memoization在大多数情况下是合理的(rtpg)。
6. React的易用性与学习曲线
- 观点:React的易用性取决于对引用相等性的理解。
- 论据:
- 理解
{} !== {}是掌握React的关键(zeorin)。 - React的复杂性使得初学者难以快速上手(nemothekid)。
- 理解
总结来看,评论者对React的Hooks和memoization持有不同看法,部分人认为它们是必要的优化手段,而另一些人则认为它们增加了不必要的复杂性。同时,React的设计模式也被认为存在根本性问题,可能为其他框架的崛起创造机会。