react useDeferredValue和useTransition有啥区别

useDeferredValueuseTransition 都是 React 18 为并发渲染 设计的核心 Hook,目的都是「避免耗时更新阻塞页面、提升用户交互体验」,但二者的核心定位、使用方式、作用对象完全不同------简单来说:

  • useDeferredValue 是「延迟更新某个值」(针对数据/状态);
  • useTransition 是「标记某个更新为低优先级」(针对更新操作)。

下面用「核心区别+场景示例+对比表」的方式讲透,新手也能快速区分。


一、核心区别(先抓本质)

特性 useDeferredValue useTransition
核心作用 对已有值创建一个"延迟更新的副本",高优先级更新(如输入)优先执行,低优先级的副本稍后更新 把状态更新标记为"非紧急更新"(transition),不阻塞高优先级交互(如输入、点击)
作用对象 针对「值/状态本身」(如列表数据、筛选结果) 针对「状态更新操作」(如 setState 调用)
使用方式 基于已有值生成延迟值,直接使用这个延迟值渲染 包裹 setState 调用,标记为低优先级更新
返回值 返回延迟更新的"副本值" 返回 [isPending, startTransition](加载状态 + 触发低优先级更新的函数)
触发时机 高优先级更新完成后,自动更新延迟值 手动调用 startTransition 触发低优先级更新
核心场景 「数据派生」场景(如输入框筛选长列表) 「状态更新」场景(如点击按钮切换大量数据、表单提交)

二、逐个拆解:用法+场景(实战理解)

1. useDeferredValue:延迟更新"派生值"(被动)

核心逻辑:当某个值(如输入框内容)频繁更新时,基于它生成一个"延迟更新的副本",组件优先渲染最新的原始值(保证交互不卡),等主线程空闲后再渲染延迟值(更新最终结果)。

典型场景:输入框实时筛选长列表(输入是高优先级,列表筛选是低优先级)。

jsx 复制代码
import { useState, useDeferredValue } from 'react';

function SearchList() {
  // 1. 高优先级:输入框内容(实时更新,不阻塞)
  const [inputValue, setInputValue] = useState('');
  
  // 2. 低优先级:延迟更新的筛选关键词(等输入完成后再更新)
  const deferredValue = useDeferredValue(inputValue, {
    timeoutMs: 200 // 可选:延迟最长等待时间(超过则强制更新)
  });

  // 3. 基于延迟值筛选列表(耗时操作,不会阻塞输入)
  const filteredList = useMemo(() => {
    // 模拟长列表筛选(耗时计算)
    return longList.filter(item => item.includes(deferredValue));
  }, [deferredValue]); // 依赖延迟值,而非原始输入值

  return (
    <div>
      {/* 输入框:实时响应,不卡顿 */}
      <input 
        value={inputValue} 
        onChange={(e) => setInputValue(e.target.value)} 
        placeholder="搜索..."
      />
      {/* 列表:基于延迟值渲染,避免输入时卡顿 */}
      <ul>
        {filteredList.map(item => <li key={item}>{item}</li>)}
      </ul>
    </div>
  );
}

// 模拟10万条长列表(用于测试耗时)
const longList = Array.from({ length: 100000 }, (_, i) => `item-${i}`);

关键效果 :输入时,输入框实时显示内容(高优先级),列表筛选会"延迟一步"更新,但输入操作完全不卡;若没有 useDeferredValue,输入时列表实时筛选会阻塞主线程,导致输入卡顿。

2. useTransition:标记"低优先级更新"(主动)

核心逻辑 :把耗时的 setState 包裹在 startTransition 中,标记为"非紧急更新",React 会优先处理高优先级交互(如输入、点击),等主线程空闲后再执行这个更新,同时通过 isPending 显示加载状态。

典型场景:点击按钮切换大量数据(如切换分页、展开长列表)。

jsx 复制代码
import { useState, useTransition } from 'react';

function BigList() {
  const [list, setList] = useState([]);
  // 1. 获取加载状态 + 低优先级更新函数
  const [isPending, startTransition] = useTransition({
    timeoutMs: 300 // 可选:超过300ms仍未执行则强制更新
  });

  // 2. 点击按钮触发低优先级更新(不阻塞页面)
  const loadBigList = () => {
    // 包裹耗时的 setState,标记为 transition
    startTransition(() => {
      // 模拟生成10万条数据(耗时操作)
      const newList = Array.from({ length: 100000 }, (_, i) => `item-${i}`);
      setList(newList); // 低优先级更新,不阻塞交互
    });
  };

  return (
    <div>
      {/* 按钮:点击后立即响应,不卡顿 */}
      <button onClick={loadBigList} disabled={isPending}>
        {isPending ? '加载中...' : '加载10万条数据'}
      </button>
      {/* 列表:加载完成后渲染,期间页面不卡 */}
      <ul>
        {list.map(item => <li key={item}>{item}</li>)}
      </ul>
    </div>
  );
}

关键效果:点击按钮后,按钮立即显示"加载中"(高优先级),生成10万条数据的更新被标记为低优先级,期间可以正常操作页面(如滚动、输入),不会因耗时计算阻塞。


三、什么时候用哪个?(快速决策)

用 useDeferredValue 的场景

✅ 有一个「源值」(如输入框内容),基于它派生另一个「耗时计算的值」(如筛选列表);

✅ 希望"源值实时更新,派生值延迟更新",避免派生计算阻塞源值的交互;

✅ 核心是"被动延迟值的更新",无需手动触发。

用 useTransition 的场景

✅ 主动触发一个「耗时的状态更新」(如 setState 生成大量数据);

✅ 希望这个更新不阻塞高优先级交互,且需要显示加载状态;

✅ 核心是"主动标记更新为低优先级",手动控制更新时机。

二者可配合使用的场景

比如"输入框筛选长列表"既可以用 useDeferredValue,也可以结合 useTransition 优化:

jsx 复制代码
// 混合使用:输入延迟 + 更新标记为低优先级
const [inputValue, setInputValue] = useState('');
const deferredValue = useDeferredValue(inputValue);
const [isPending, startTransition] = useTransition();

const handleInput = (e) => {
  // 输入更新标记为高优先级
  setInputValue(e.target.value);
  // 筛选更新标记为低优先级
  startTransition(() => {
    // 基于 deferredValue 筛选列表
  });
};

四、关键注意事项

  1. 都不改变计算耗时:二者只是"调整更新优先级",不会减少筛选/生成数据的耗时,只是让耗时操作不阻塞交互;
  2. timeoutMs 兜底 :都支持 timeoutMs 参数,超过该时间后,无论主线程是否空闲,都会强制更新(避免延迟太久影响体验);
  3. 仅 React 18+ 支持:二者都是 React 18 并发渲染的特性,低版本无法使用;
  4. 不要滥用:仅用于"耗时更新"场景,普通更新(如按钮计数)无需使用,反而增加开销。

总结

  1. 核心区别
    • useDeferredValue:延迟「值的更新」,被动适配源值变化;
    • useTransition:标记「更新操作」为低优先级,主动控制更新时机;
  2. 记忆口诀
    • 「派生值延迟」用 useDeferredValue(如输入筛选);
    • 「更新操作标记」用 useTransition(如按钮加载数据);
  3. 核心目标:都是为了在并发渲染中,保证高优先级交互(输入、点击)不被低优先级耗时更新阻塞,提升用户体验。

如果有具体场景(比如"输入筛选长列表卡顿""切换标签页加载大量数据阻塞"),可以告诉我,我帮你写针对性的优化代码。

相关推荐
梦想的旅途22 小时前
企业微信自动化操作的高效实现方案
开发语言·javascript·ecmascript
发现一只大呆瓜9 小时前
SSO单点登录:从同域到跨域实战
前端·javascript·面试
发现一只大呆瓜9 小时前
告别登录中断:前端双 Token无感刷新
前端·javascript·面试
Cg1362691597410 小时前
JS-对象-Dom案例
开发语言·前端·javascript
lxh011311 小时前
数据流的中位数
开发语言·前端·javascript
zadyd12 小时前
Workflow or ReAct ?
前端·react.js·前端框架
雨雨雨雨雨别下啦15 小时前
Vue案例——面经
前端·javascript·vue.js
从文处安15 小时前
「前端何去何从」(React教程)React 状态管理:从局部 State 到可扩展架构
前端·react.js
Wect16 小时前
React Scheduler & Lane 详解
前端·react.js·面试