React中startTransition与useDeferredValue为何表现差异巨大?
React中useDeferredValue与自定义实现的渲染差异问题
我在代码库中大量使用useDeferredValue(),原本以为它能完全支持并发模式,但实际发现它并不会把渲染拆分为小块;而被startTransition包裹的setState触发的渲染会拆分成5ms的块。
在React DevTools中观察到:普通setState触发的渲染优先级为immediate,useDeferredValue或startTransition+setState触发的渲染优先级都是normal,所以我之前一直以为后两者的行为是一致的。
于是我尝试把所有useDeferredValue调用替换成自定义的useDeferredValue2:
function useDeferredValue2(val) { const [deferred, setDeferred] = useState(val); useEffect(() => { React.startTransition(() => { setDeferred(val); }); }, [val]); return deferred; }
替换后发现,useDeferredValue2触发的所有渲染都会拆分为5ms块,和原生useDeferredValue的差异很明显。那是不是原生useDeferredValue基本等同于下面这个useDeferredValue3?
function useDeferredValue3(val) { const [deferred, setDeferred] = useState(val); useEffect(() => { setDeferred(val); }, [val]); return deferred; }
想请教:在React内部,useDeferredValue、useDeferredValue2和useDeferredValue3触发的渲染存在哪些差异?
三者的渲染行为差异
1. 原生useDeferredValue
- 属于渲染阶段的同步延迟计算:当输入值
val变化时,React会在当前渲染周期内直接计算并返回延迟值,不会触发额外的重渲染。 - 它的“延迟”仅体现在优先级让步:如果有更高优先级的任务(比如用户输入),React会暂停这个低优先级的计算,优先处理高优先级任务;但一旦开始执行,会一次性完成渲染,不会拆分为5ms的块,这就是看不到分片的核心原因。
- 优先级标记为
normal,但属于渲染阶段的优先级,而非状态更新阶段的优先级。
2. useDeferredValue2(基于startTransition的自定义实现)
- 触发额外的可中断并发更新:通过
useEffect在渲染完成后,用startTransition包裹setDeferred,这会将这次状态更新标记为“可中断”的低优先级任务。 - React会将渲染拆分为多个5ms的小块,利用浏览器空闲时间执行,一旦有更高优先级的任务(比如用户交互)就会暂停当前渲染,优先处理高优先级任务,这就是能看到渲染分片的原因。
- 这里的
normal优先级是状态更新的优先级,且带有并发可中断标记。
3. useDeferredValue3(基于useEffect的简单实现)
- 本质是渲染完成后的普通同步更新:
useEffect触发的setDeferred是一次普通的状态更新,优先级为normal但没有并发可中断标记。 - React会一次性完成这次渲染,不会进行分片;如果此时有高优先级任务,必须等这次渲染完成后才能处理,无法中断。
- 和原生
useDeferredValue的核心区别:原生是渲染阶段同步计算值,无额外重渲染;它是渲染完成后触发额外重渲染,多一次渲染周期。
内容的提问来源于stack exchange,提问作者Leo Jiang
相关产品推荐
相关产品推荐

