第一个可以用在条件语句中的nativehook诞生了
时间:2023-03-28 17:53:23
HTML
大家好,我是Kason。在10月13日的first-class-support-for-promisesRFC中,引入了一个新的钩子——use。用什么?就是use,这个钩子就叫use。这也是第一个:可以写在条件语句中的Hooks可以写在其他hook回调中的Hooks本文将讨论这个特定的hook。欢迎加入人类优质前端框架群。我们知道有什么用。async函数会和await关键字一起使用,比如:asyncfunctionload(){const{name}=awaitfetchName();returnname;}类似,在React组件中,可以使用use实现类似的效果,例如:functionCpn(){const{name}=use(fetchName());return
{name}
;}可以考虑,使用的功能类似于:asyncawait中awaitgenerator中的yielduse作为读取异步数据的原语,可以配合Suspense实现逻辑数据请求、加载和返回。例如下面的例子,当fetchNote执行异步请求时,loading状态会由Suspense组件包装Note来渲染。请求成功后会重新渲染,笔记数据会正常返回。当请求失败时,错误逻辑将由封装在Note中的ErrorBoundary组件处理。functionNote({id}){constnote=use(fetchNote(id));return(
{note.title}
);}背后的实现原理并不复杂:Note组件渲染时fetchNote第一次发起请求,会抛出一个promise,中断渲染过程,使用Suspensefallback作为渲染结果。当promise状态发生变化时,根据note的返回值重新触发渲染rendering实际上,这套基于promise的中断和重新渲染流程目前是存在的。use的存在是为了代替上面的过程。与上面提到的在当前React中已经存在的promise过程不同,use只是一个原语(primitives),并不是一个完整的处理过程。例如,use没有缓存promise的能力。例如下面的代码中,fetchTodo执行后会返回一个promise,use会消费这个promise。异步函数fetchTodo(id){constdata=awaitfetchDataFromCache(`/api/todos/${id}`);返回{contents:data.contents};}functionTodo({id,isSelected}){consttodo=use(fetchTodo(id));return(
{todo.contents} );}当Todo组件的idprop发生变化时,逻辑触发fetchTodo重新请求。但是当isSelected属性改变时,Todo组件也会重新渲染,fetchTodo执行后会返回一个新的promise。返回一个新的promise不一定会产生一个新的request(取决于fetchTodo的实现),但是肯定会影响React接下来的运行过程(比如性能优化打不中)。这时候就需要配合React(也在RFC中)提供的缓存API。在下面的代码中,如果id属性保持不变,fetchTodo总是返回相同的承诺:constfetchTodo=cache(async(id)=>{constdata=awaitfetchDataFromCache(`/api/todos/${id}`);返回{内容:data.contents};});use的潜在作用目前use的应用场景仅限于wrappingpromise。但以后use将是客户端处理异步数据的主要手段,例如:处理contextuse(Context)可以达到和useContext(Context)一样的效果,不同的是前者可以在条件语句中执行和其他钩子回调。要处理状态,您可以使用use来实现新的原生状态管理解决方案:constcurrentState=use(store);constlatestValue=使用(可观察);whynotuseasyncawait正如本文开头提到的,useprimitive类似于asyncawait中的await,那么Whynotjustuseasyncawait?像这样://Note是一个React组件asyncfunctionNote({id,isEditing}){constnote=awaitdb.posts.get(id);return(
{note.title}
{isEditing?:null});}有两个原因。一方面,异步等待的工作方式与React客户端处理异步时的逻辑不同。当await请求解析时,调用堆栈继续从await语句开始执行(生成器中的yield也是如此)。在React中,更新过程是从根组件开始的,所以当返回数据时,更新过程是从根组件开始的。改用asyncawait势必会给当前的React底层架构带来挑战。最起码会对性能优化产生相当大的影响。另一方面,asyncawait接下来会在ServerComponent中实现,它是一个异步服务端组件。服务端组件和客户端组件都是React组件,只是前者渲染在服务端(SSR),后者渲染在客户端(CSR)。如果两者都使用asyncawait,那么从代码层面就不容易区分两者。总结一下,use是一个读取异步数据的原语,它的出现是为了规范React在客户端处理异步数据的方式。由于是原语,所以它的功能很底层,比如不包括请求的缓存功能(由缓存处理)。这样设计的原因是React团队不希望开发者直接使用它们。这些原语的受众是React生态系统中的其他库。比如像SWR、React-Query这样的请求库可以结合使用,再结合自己实现的请求缓存策略(而不是使用React提供的缓存方法)。各种状态管理库也可以使用use作为其底层状态单元的容器。值得吐槽的是,Hooks文档中关于hook限制的部分可能要重写了。