React 并发特性实战:用 useTransition 与 useDeferredValue 让 UI 保持响应

test12026-09-130 次阅读

问题背景

在列表搜索、大数据过滤这类交互里,用户输入触发的重计算会占用主线程,导致输入框卡顿、打字掉帧。React 18 引入的并发特性把更新分为"紧急"与"过渡"两类,让用户输入这类紧急更新优先渲染,重计算放到后台。

核心 API

  • useTransition:返回一个 [isPending, startTransition],把非紧急更新包进 startTransition。
  • useDeferredValue:直接延迟某个派生值的更新,等价但更简洁。
  • useMemo:配合延迟值做缓存,避免每次输入都重算。

可运行示例

import { useState, useTransition, useDeferredValue, useMemo } from 'react';

const ALL = Array.from({ length: 20000 }, (_, i) => ({
  id: i,
  name: `User-${i}`,
}));

export function UserFilter() {
  const [query, setQuery] = useState('');
  const [isPending, startTransition] = useTransition();

  // 输入框立即更新;过滤用的 query 延迟到空闲时再变
  const deferredQuery = useDeferredValue(query);

  const list = useMemo(
    () => ALL.filter((u) =>
      u.name.toLowerCase().includes(deferredQuery.toLowerCase())),
    [deferredQuery],
  );

  function onChange(e: React.ChangeEvent<HTMLInputElement>) {
    const v = e.target.value;
    setQuery(v); // 紧急更新:输入框不卡
    startTransition(() => {
      // 此处可做额外重计算,React 会在空闲时提交
    });
  }

  return (
    <div>
      <input value={query} onChange={onChange} placeholder="搜索用户" />
      {isPending && <span>过滤中…</span>}
      <ul>
        {list.slice(0, 100).map((u) => (
          <li key={u.id}>{u.name}</li>
        ))}
      </ul>
    </div>
  );
}

要点与坑

  • useDeferredValue 的旧值会先渲染,输入期间不会卡白屏,仅在"慢"的派生结果就绪后替换。
  • 不要在 startTransition 里放 setState 之外的副作用;副作用仍需用 useEffect 管理。
  • 过渡更新可被更高优先级的输入打断,因此不要依赖过渡内状态的"最终一致性"做关键流程。
  • 列表渲染配合虚拟化的 windowing 方案效果最佳,二者正交可叠加。
T

test1

文章作者

问题背景 在列表搜索、大数据过滤这类交互里,用户输入触发的重计算会占用主线程,导致输入框卡顿、打字掉帧。React 1...

分类
技术
发布时间
2026-09-13
字数
约 1533 字
阅读
0 次