当前位置: 首页 > Web前端 > HTML

React18中的useTransition和useDeferredValue使用

时间:2023-03-28 17:07:28 HTML

React18引入了一个关键概念并发(Concurrency)。并发涉及同时执行多个更新操作,这可以说是React18中最重要的特性。除了并发,React18还新增了两个hook,分别是useTransition和useDeferredValue。它们都是为了降低更新操作的优先级,但问题是,什么时候使用它们呢?并发(Concurrent)在实现“并发”之前,渲染是同步的(所谓同步就是react的一个组件如果长时间执行,是不能被打断的,会一直执行到组件完全渲染完进入DOM。在这个过程中,由于Javascript是单线程的,渲染任务会占用JavaScript线程,阻塞浏览器主线程,从而阻止用户进行交互操作)。但是并发渲染(并发指的是通过时间片将任务拆分成多个,然后react根据优先级完成调度策略,先挂起低优先级的任务,将高优先级的任务分配给在一帧的空闲时间浏览器主线程,如果浏览器在当前帧还有空闲时间剩余,那么React会利用空闲时间执行剩余的低优先级任务),react渲染和更新可以中断和恢复。然后,如果在执行组件更新期间有新的更新请求到达。比如我们下面的input输入事件,那么React就会创建一个新的更新版本。在这种情况下,一定时间内可能会同时存在多个更新版本。为了优化上述问题,React18提供了一个新的Hook函数useTransition,可以将多个版本的更新打包在一起,并在未来某个帧空闲时间内执行,从而优化应用的性能和响应时间。useDeferredValue的作用是将某个值的更新推迟到未来某个时间片,从而避免不必要的重复渲染和性能开销。在不使用任何优化的情况下,同步更新假设我们有一个包含从0到19,999的数字的数组。这些数字在用户界面上显示为列表。UI还有一个文本框,允许我们过滤数字。例如,我可以通过在文本框中输入数字99来过滤掉以99开头的数字。import{useState,useTransition}from"react";constnumbers=[...newArray(20000).keys()];exportdefaultfunctionApp(){const[query,setQuery]=useState("");consthandleChange=(e)=>{setQuery(e.target.value);};return(

{numbers.map((i,index)=>(query?i.toString().startsWith(query)&&{i}

:{i}

))}
);}因为有20,000个元素数组,过滤将是一个有点耗时的过程。当我们尝试在文本框中输入数字时,我们可以观察到这一点。输入的值出现在文本框中有延迟,因为每次击键后渲染都需要一些时间。useTransition接下来我们使用useTransition修改上面的代码functionApp(){const[query,setQuery]=useState("");const[isPending,startTransition]=useTransition();consthandleChange=(e)=>{startTransition(()=>{setQuery(e.target.value);});};constlist=useMemo(()=>(numbers.map((i,index)=>(query?i.toString().startsWith(query)&&{i}

:{i}

))),[查询]);return(
{isPending?"Loading...":list}
);}从上面你可以看到useTransation返回一个包含两个子项的数组。isPending:告诉你是否有一些更新操作还在等待(还没有被React执行,以较低的优先级处理)startTransition:React将以较低的优先级调度它包装的更新操作。这样就保证了用户和输入框的交互保持流畅。然后用isPending判断UI是否可以更新。useDeferredValueuseDeferredValue的作用与useTransition相同,都是用来更新状态而不阻塞UI。但使用场景不同。useTransition是为了让您完全控制应该以较低优先级安排哪个更新操作。然而,在某些情况下,实际的更新操作可能无法访问(例如,状态是从父组件上传的)。这时候可以使用useDeferredValue代替。用React团队成员Dan的话说,useDeferredValue主要是:当值“来自上面”并且你实际上无法控制相应的setState调用时有用。它的意思是:当值来自“上面”,而你实际上在你无法控制相应的setState调用时,这个方法很有用。这更符合我们上面举例的场景。那么我们需要将上面的例子改成如下:import{useState,useMemo,useDeferredValue}from"react";constnumbers=[...newArray(200000).keys()];导出默认函数App(){const[query,setQuery]=useState("");consthandleChange=(e)=>{setQuery(e.target.value);};返回(
);}functionList(props){const{query}=道具;constdefQuery=useDeferredValue(查询);constlist=useMemo(()=>(numbers.map((i,index)=>(defQuery?i.toString().startsWith(defQuery)&&{i}

:{i}

))),[defQuery]);return(
{list}
);}总结上面说了useTransition直接控制更新状态的代码,而useDeferredValue控制一个受状态变化影响的值,它们做同样的事情,有助于改善用户体验(UX),不应同时使用。相反,如果您有权访问更新操作并且有一些应该以较低优先级处理的更新操作,请使用useTransition。如果您没有此权限,请使用useDeferredValue。参考useTransition和useDeferredValueinReact18UseTransition()VsUseDeferredValue()InReact18