React如何原生实现防抖?
作者:卡颂
简介:《React技术揭秘》作者
来源:SegmentFault 思否社区
大家好,我卡颂。
作为前端,想必你对防抖(debounce)、节流(throttle)这两个概念不陌生。
在React18中,基于新的并发特性,React原生实现了防抖的功能。
今天我们来聊聊这是如何实现的。
useTransition Demo
useTransition是一个新增的原生Hook,用于以较低优先级执行一些更新。
在我们的Demo中有ctn与num两个状态,其中ctn与输入框的内容受控。
当触发输入框onChange事件时,会同时触发ctn与num状态变化。其中触发num状态变化的方法(即updateNum)被包裹在startTransition中:
function App() {
const [ctn, updateCtn] = useState('');
const [num, updateNum] = useState(0);
const [isPending, startTransition] = useTransition();
return (
<div >
<input value={ctn} onChange={({target: {value}}) => {
updateCtn(value);
startTransition(() => updateNum(num + 1))
}}/>
<BusyChild num={num}/>
</div>
);
}
num会作为props传递给BusyChild组件。在BusyChild中通过while循环人为增加组件render所消耗的时间:
const BusyChild = React.memo(({num}: {num: number}) => {
const cur = performance.now();
// 增加render的耗时
while (performance.now() - cur < 300) {}
return <div>{num}</div>;
})
所以,在输入框输入内容时能明显感到卡顿。
在线示例地址:https://codesandbox.io/s/immutable-glade-u0m6vv
按理说,onChange中会同时触发ctn与num的状态变化,他们在视图中的显示应该是同步的。
然而实际上,输入框连续输入一段文字(即ctn的状态变化连续展示在视图中)后,num才会变化一次。
如下图,初始时输入框没有内容,num为0:
输入框输入很长一段文字后,num才变为1:
这种效果就像:被startTransition包裹的更新都有防抖的效果一样。
这是如何实现的呢?
什么是lane
在React18中有一套更新优先级机制,不同地方触发的更新拥有不同优先级。优先级的定义依据是符合用户感知的,比如:
用户不希望输入框输入文字会有卡顿,所以onChange事件中触发的更新是同步优先级(最高优)
用户可以接受请求发出到返回之间有等待时间,所以useEffect中触发的更新是默认优先级
const SyncLane = 0b0000000000000000000000000000001;
const DefaultLane = 0b0000000000000000000000000010000;
// 用“按位或”操作合并lane
const lanes = SyncLane | DefaultLane;entangle机制
可以看到,lane机制本质上就是各种位运算,可以设计的很灵活。
onChange={({target: {value}}) => {
updateCtn(value);
startTransition(() => updateNum(num + 1))
}
updateCtn(value)由于在onChange中触发,优先级为SyncLane
updateNum(num + 1)由于在startTransition中触发,优先级为TransitionLanes中的某一个
SyncLane由于是最高优先级,会被执行,所以我们会看到输入框中内容变化
TransitionLanes相关lane优先级比SyncLane低,暂时不会执行,同时他们会产生纠缠
总结
lane模型
entangle机制
更新过期机制