Blog.

深入理解 useTransition

LiuuY

useTransition 很难用

React 官方文档中关于 useTransition 的例子,我们首先点击 Post (slow) 按钮,马上再点击其他按钮,由于按钮点击响应事件使用了 startTransition,从而不会因为 <PostsTab /> 组件耗时而导致的冻结用户界面。

但是,我们注意到,例子中,将耗时组件 <PostsTab /> 使用 memo 包起来了。如果我们删除 memo,按照上述的操作首先点击 Post (slow) 按钮,等到 Posts 加载出来后,再点击其他按钮,明显页面卡顿了,仅仅是删除了 memouseTransition 就「失效」了 😭🤔️。

useTransition 会导致渲染两次

我们需要知道的是,调用 useTransition 时,会触发两次渲染,首先优先级高的任务会先渲染(例如点击按钮事件),然后才会进行 startTransition 回调中触发的优先级低的渲染。

在例子中,当我们在父组件 TabContainer 中,每次点击按钮时(即调用了 startTransition),就会导致两次渲染。

所以,当我们处于 Post (slow) 按钮时,再点击其他按钮,导致 TabContainer 首先重新渲染,即会导致非常耗时的 <PostsTab /> 组件重新渲染(这也就是 React 文档中,为什么需要使用 memo 而防止这个两次渲染导致的问题)。

我们在代码中加入 <Profiler />,分别在按钮、下面内容和 App 本身渲染时打印日志,我们可以看到,当处于 Post (slow) 按钮时,再点击其他按钮,会导致渲染两次。

如何正确的使用 useTransition

  1. 按照 React 文档中例子,配合 memo 使用(虽然文档中并没有明说🤔️)。

  2. useTransition 放到子组件中,这样由 useTransition 而导致的二次渲染,就仅仅是渲染 <TabButton /> 这个轻量级组件两次。

当处于 Post (slow) 按钮时,再点击其他按钮,console 打印的渲染日志,可见只有 <TabButton /> 渲染了两次:

  1. 只在耗时组件使用 useTransition,即仅当点击 Post (slow) 按钮时,这样再点击其他按钮就并不会因为 useTransition 而导致的二次渲染。

当处于 Post (slow) 按钮时,再点击其他按钮,console 打印的渲染日志:

总结

useTransition 会导致组件二次渲染,所以使用时需要特别注意子组件二次渲染导致的卡顿。