这是react18提供的一个hook,官方描述它的作用是可以延迟更新某一部分UI。实际上就是返回一个更新时机落后于传入值的值,也就是延迟的值,听上去很拗口,我们通过一个例子和源码来学习下这个hook。
export default function MyComponent() {
const [inputValue, setInputValue] = useState("");
const deferredValue = useDeferredValue(inputValue);
let start = performance.now();
while (performance.now() - start < 1000) {}
return (
<div>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<p>即时值: {inputValue}</p>
<p>延迟值: {deferredValue}</p>
</div>
);
}
在上面的例子中,手动的减慢了组件的渲染,这样更容易看到即时更新的值和延迟值的区别。当键盘输入时,可以明显看到页面上即时值先进行了更新,而延迟值等了一会儿才在页面上更新。也就是我们输入了一次,但是页面触发了两次更新,第一次更新仅仅更新了inputValue,第一次更新完成后立马进行了第二次更新,这次更新才更新了deferredValue。
源码
我们结合源码就更清晰了。为了方便理解我对源码进行了一部分改动
function useDeferredValue<T>(value: T): T {
const [prevValue, setValue] = useState(value);
useEffect(() => {
const prevTransition = ReactCurrentBatchConfig.transition;
ReactCurrentBatchConfig.transition = {};
try {
setValue(value);
} finally {
ReactCurrentBatchConfig.transition = prevTransition;
}
}, [value]);
return prevValue;
}
可以看到本质上就是通过useState和useEffect来实现的,它内部通过useState保存了我们往useDeferredValue传递的初始值,当value发生变化,通过useEffect再次触发页面渲染。在setValue之前,还有两行代码
const prevTransition = ReactCurrentBatchConfig.transition;
ReactCurrentBatchConfig.transition = {};
这两行代码和transition相关,这里可以简单的理解为,通过这两行代码,让后面的更新使用transition优先级的方式进行,transition优先级的更新更容易被打断。
我们再次走之前例子的流程,当input输入时,调用setInputValue,进入第一次渲染阶段,由于setInputValue通过change事件触发,此时优先级较高。渲染完成后触发了useEffect回调,通过transition优先级也就是较低的优先级再次触发渲染,这时才会更新延迟值
总结
通过上面的分析,我们可以总结出useDeferredValue什么时候使用:当页面上某一部分的UI的渲染可以被延迟或被更重要的任务打断时,可以通过这个hook去降低这一部分渲染的优先级