人人都会AI编程

11.3 自定义 Hooks 与 Mixin 混入的对比与优势

更新时间:2026-07-10

在 React 18 之前,所有的状态更新都是“紧急的”——一旦触发更新,React 会立即开始渲染,中断当前页面操作,直接反映新状态到屏幕上。这在多数场景下没有问题,但碰到复杂计算或大数据渲染时,页面会出现明显的卡顿或延迟,影响交互体验。

React 18 引入了 Transitions 机制,允许开发者将某些更新标记为非紧急(低优先级),从而把宝贵的计算资源优先分配给用户的直接交互(如输入、点击),让应用保持流畅响应。

11.3.1 什么是“紧急更新”和“非紧急更新”

  • 紧急更新(Urgent updates):直接响应用户交互,比如键盘输入、按钮点击、拖拽等。用户期望这些操作立刻反馈,任何延迟都会感觉“卡顿”。
  • 非紧急更新(Transition updates):UI 从一个视图过渡到另一个视图,比如筛选后的列表结果、切换选项卡的内容、搜索结果展示。这类更新允许有一点延迟,用户愿意等待片刻以换取更流畅的整体体验。

React 通过 startTransition API 和 useTransition Hook 来区分这两类更新。

11.3.2 基本用法:startTransition

startTransition 是一个从 react 导入的顶层函数,用于包裹非紧急的状态更新:

import { useState, startTransition } from 'react';

function FilterableList() {
  const [query, setQuery] = useState('');
  const [filtered, setFiltered] = useState(allItems);

  const handleChange = (e) => {
    const value = e.target.value;
    // 紧急更新:输入框的显示必须立刻响应
    setQuery(value);

    // 非紧急更新:筛选列表可以稍后处理
    startTransition(() => {
      const result = allItems.filter(item => item.includes(value));
      setFiltered(result);
    });
  };

  return (
    <>
      <input value={query} onChange={handleChange} />
      <ExpensiveList items={filtered} />
    </>
  );
}

当用户在输入框中快速打字时,setQuery 是紧急更新,输入框中的文字会立即改变。而被 startTransition 包裹的 setFiltered 则被标记为“过渡任务”,React 会在空闲时处理,并且如果用户继续输入,之前的过渡任务可以被中断并丢弃,直接执行最新的过渡任务。这样界面上不会出现由于计算量大导致的输入卡顿。

11.3.3 useTransition Hook

useTransition 返回一个数组:[isPending, startTransition]

  • isPending:布尔值,表示当前是否还有尚未完成的过渡更新,可用于在等待期间显示加载状态。
  • startTransition:与顶层的 startTransition 相同,但关联了 isPending 状态。
import { useState, useTransition } from 'react';

function SearchResults() {
  const [query, setQuery] = useState('');
  const [result, setResult] = useState(initialData);
  const [isPending, startTransition] = useTransition();

  const handleChange = (e) => {
    const value = e.target.value;
    setQuery(value); // 紧急更新

    startTransition(() => {
      setResult(fetchSearchResults(value)); // 模拟开销大的更新
    });
  };

  return (
    <div>
      <input value={query} onChange={handleChange} />
      {isPending && <Spinner />}
      <ResultsList data={result} />
    </div>
  );
}

当用户快速输入时,isPending 会变为 true,可以显示一个加载指示器。但这个指示器只会在过渡更新真正花费较长时间时才会被用户感知,在大多数性能好的设备上几乎瞬间完成。

11.3.4 Transitions 的底层原理

React 18 的并发渲染(Concurrent Rendering)是 Transitions 的基础。并发模式下,React 可以将渲染任务拆分为小的时间片,并赋予不同优先级。

  • 紧急更新(如 setState 直接调用)被放在同步通道或高优先级任务中,立即调度。
  • Transition 更新被标记为低优先级任务。当高优先级任务(如用户输入)到达时,正在进行的过渡渲染可以被中断,React 会丢弃当前 workInProgress 树,从新的状态重新开始渲染。
  • 这种“可中断”特性保证了用户交互的即时响应,而复杂的视图更新可以在浏览器空闲时段逐步完成。

11.3.5 适用场景与最佳实践

适用场景:

  • 搜索输入框:输入文字紧急更新,搜索结果列表过渡更新。
  • 选项卡切换:切换标签时,内容区的复杂组件可以不阻塞选项卡的点击反馈。
  • 大列表筛选、排序:保持按钮或下拉框的即时响应,让重渲染在后台进行。
  • 路由导航:React Router 的 useNavigate 内部也可配合 Transitions 使用,使页面切换不卡顿。

注意事项:

  • startTransition 内部必须是同步的状态更新调用,不能是异步操作。
  • 不要将所有更新都包裹在 Transitions 中,只适用于确实存在性能问题或需要区分优先级的场景。
  • 过渡更新必须是纯状态更新,如果包含副作用(如请求数据),请将异步请求放在 useEffect 或其他地方,Transition 只负责根据已有的数据更新状态。
  • Suspense 搭配使用时,过渡更新中的 Suspense 边界不会显示之前的回退 UI,而是保持当前界面直到新内容就绪,避免了闪烁。

11.3.6 实际收益

使用 Transitions 后,应用在复杂交互下的主观流畅度明显提升。用户会感觉页面“跟手”,即便后台正在进行大量计算。这是 React 从“性能优化”向“体验优化”迈进的重要一步,也是 React 18 并发渲染最亮眼的功能之一。

在代码实战中,你可以先确认哪些更新确实引发了卡顿,然后引入 useTransition,通过 isPending 给用户一个轻量的 Loading 提示,让体验更完整。