大家好,我是Kason。作为前端,你一定熟悉debounce和throttle的概念。在React18中,基于新的并发特性,React原生实现了防抖功能。今天我们就来说说这是如何实现的。useTransitionDemouseTransition是一个新的原生Hook,用于“以较低的优先级执行一些更新”。在我们的Demo中,有ctn和num两种状态,其中控制ctn和输入框的内容。当触发输入框的onChange事件时,会同时触发ctn和num的状态变化。其中,“触发num状态变化的方法”(即updateNum)被包裹在startTransition中:functionApp(){const[ctn,updateCtn]=useState('');const[num,updateNum]=useState(0);const[isPending,startTransition]=useTransition();return(
{updateCtn(value);startTransition(()=>updateNum(num+1))}}/>
);}num将作为props传递给BusyChild组件。通过while循环人为增加BusyChild中componentrender消耗的时间:constBusyChild=React.memo(({num}:{num:number})=>{constcur=performance.now();//增加时间ofrender耗时while(performance.now()-cur<300){}return
{num}
;})因此,在输入框输入内容时,可以明显感觉到卡顿。在线示例地址[1]。按理说ctn和num的状态变化会在onChange中同时触发,它们在视图中的显示应该是同步的。但实际上,在输入框中连续输入一段文字后(即在视图中连续显示ctn的状态变化),num只会变化一次。如下图,初始输入框没有内容,num为0:输入框输入一长段文字后,num变为1:这个效果就像:startTransition包裹的update效果为“防抖”。这是如何实现的?什么是车道?在React18中,有一个“更新优先机制”。不同地方触发的更新有不同的优先级。定义优先级的依据是符合用户感知的。比如用户不希望输入框在输入文字时卡住,那么在onChange事件中触发的更新就是同步优先级(最高优先级)。用户可以接受请求和返回之间有一个等待时间,所以useEffect中触发的更新是默认的优先级。那么如何表达优先级呢?使用称为lane的31位二进制文??件。例如“同步优先级”和“默认优先级”定义如下:constSyncLane=0b00000000000000000000000000000001;constDefaultLane=0b0000000000000000000000000010000;值越小,优先级越高,即SyncLane
{updateCtn(value);startTransition(()=>updateNum(num+1))}其中:updateCtn(value)在onChange中触发,优先级为SyncLane。updateNum(num+1)在startTransition中触发,优先级为TransitionLanes之一。当在输入框中重复输入文本时,会重复执行上述过程。不同的是:SyncLane会被执行,因为它的优先级最高,所以我们会看到输入框的内容变化。TransitionLanes相关的Lane优先级低于SyncLane,暂时不会执行,会造成纠缠。为了防止一个更新因为优先级低而无法执行,React有一个“过期机制”:每个更新都有一个过期时间,如果在过期时间内没有执行,就会过期。过期后的更新会同步执行(也就是说他的优先级变得和SyncLane一样)。在我们的示例中,startTransition(()=>updateNum(num+1))将生成许多相互交织的TransitionLanes相关通道。一段时间后,其中一个通道到期,因此其优先级提高到与SyncLane相同,并立即执行。又因为这个lane和其他TransitionLanes相关的lanes纠缠在一起,所以会一起执行。表现为:在输入框不停地打字,但是视图中num显示的数字过一会就变了。小结今天我们讲了useTransition的一些内部实现,涉及:车道模型。纠缠机制。更新过期机制。最有意思的是,由于不同电脑的性能不同,浏览器帧率会发生变化,所以React会动态调整不同电脑的防抖效果。这相当于不需要你手动设置debounce的时间参数,React会根据电脑的性能动态调整。参考[1]在线示例地址:https://codesandbox.io/s/immutable-glade-u0m6vv?file=/src/App.js。