Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

useMemo vs. useEffect + useState #53

Open
jtwang7 opened this issue Jul 27, 2022 · 0 comments
Open

useMemo vs. useEffect + useState #53

jtwang7 opened this issue Jul 27, 2022 · 0 comments
Labels

Comments

@jtwang7
Copy link
Owner

jtwang7 commented Jul 27, 2022

useMemo vs. useEffect + useState

参考文章:useMemo vs. useEffect + useState

❇️ 前言

在项目开发中突发奇想有了一个思考:当我们在数据更新触发重渲染的过程中,使用 useMemo 好还是用的比较普遍的 useEffect + useState 好呢?假设我们接收到了一个数据的变动,我们需要在该数据基础上对数据进行重新组织,然后更新视图,则以上两种方法会有各自一套不同的渲染逻辑:

  • useEffect + useState:用 useState 重新创建一个变量来维护更新后的数据,将原数据处理完毕后 setState() 存入到该变量中并触发重渲染,useEffect 捕获到依赖项发生变化,基于新数据更新视图。
  • useMemo:基于 callbcak 返回一个值作为变量进行维护,同时提供一个依赖数组选项,当依赖选项发生改变时,会同步触发 useMemo callback 重新计算并更新变量。

✅ Compare Result

🔆 useEffect + useState 会产生额外的渲染

useEffectuseState 将在每次更改时导致额外的渲染:

  • 第一次渲染将“滞后”过时的数据
  • 然后它会立即使用新数据排队进行额外的渲染

举个例子:

function expensiveCalculation(x) { return x + 1; };

我们初始化 x 为 0:

  • useMemo 版本会立即呈现 1,因为它基于 x 进行初始化并同步更新数据
  • `useEffect 版本渲染为 null,然后在组件渲染后运行效果,更改状态,并以 1 排队新的渲染。

然后,如果我们将 x 更改为 2:

  • useMemo 运行并呈现 3
  • useEffect 版本运行,再次渲染 1,然后效果触发,组件以正确的值 3 重新运行。

导致 useEffect 二次渲染而 useMemo 不发生二次渲染的原因在于:

❇️ useEffect 发生在页面渲染之后,而 useMemo 则是在页面渲染期间执行。

💡 总结

  • 就昂贵计算的运行频率而言,两者具有相同的行为
  • useEffect 版本会导致两倍的渲染量,这对性能不利。
  • useMemo 版本更简洁、更易读。它不会引入不必要的可变状态,并且移动部件更少。

因此,若只是基于已有值进行改动,并缓存改动后的结果,可以优先考虑 useMemo

useMemo 与 Vue 中的 computed 计算属性使用场景类似,除此之外,他还可以作为一种性能优化的手段。

🔆 useEffect 异步更新

虽然上述场景下 useMemo 看上去是最佳实践,但 useEffect + useState 在某些场景下仍然是较优的一个选择:

  • 复杂计算 & 长时间运行
  • 频繁更新

在一些长时间运行的同步场景中,useMemo 持有渲染线程,导致用户界面体验卡顿,而 useEffect + useState 是异步运算的,因此不会阻止渲染。

此外,useEffect + useState 可以处理一些带有异步副作用的逻辑,而这些是 useMemo 处理不了的。

参照官方文档说明:传入 useMemo 的函数会在渲染期间执行。请不要在这个函数内部执行不应该在渲染期间内执行的操作,诸如副作用这类的操作属于 useEffect 的适用范畴,而不是 useMemo

所以,当 useMemo 记忆化函数中存在副作用时,我们应当使用 useEffect + useStateuseEffect + useState 在要计算的事物是异步的情况下是正确的解决方案,因为无论如何该值在当前渲染中都不可用。

🔆 useEffectuseMemo 的触发时机

  • useEffect 是一个集体调用,无论是否异步,它是在所有组件渲染后收集的。

  • useLayoutEffect 是一个集体调用,但它同步发生在页面渲染更新前。

  • useMemo 是一个本地调用,它只与这个组件有关。可以将 useMemo 视为一个具有使用上次更新结果的赋值语句。

这意味着,useMemo 更紧急,然后是 useLayoutEffect,最后是 useEffect

💡 useEffectuseMemo 触发时机的差异,引出了另一个值得思考的点:相比于 useMemouseEffect 可以获取到更新后的 DOM 节点 (但还未可视化),因此在处理与 DOM 节点有关的操作时更具优势。

❇️ 总结

useEffect + useState

  1. 存在与 DOM 相关的逻辑操作
  2. 大计算量场景
  3. 异步副作用

useMemo

  1. 简单计算
  2. 避免二次重渲染
@jtwang7 jtwang7 added the Hooks label Jul 27, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant