大家好,我是Cason。都说Hooks是React的未来,但是Hooks的最佳实践是什么?关于这块知识,官方文档完全没有提及。所以在实际项目中,经常会出现类似下面的问题://...useEffect(()=>{fetchData(a,b).then(//...)},[a,b])//。..useEffect依赖于a和b的两种状态。当其中任何一个发生变化时,都会执行fetchData请求数据。随着应用程序变得越来越复杂,跟踪a和b何时发生变化变得越来越困难。如果调整时间接口,fetchData也需要状态c作为参数。那么跟踪状态变化的难度会进一步增加。最终会导致:无意义的fetchData被多次调用,逻辑上有难以追溯的bug。有的朋友会说:可以封装自定义的Hooks。这只是把问题隐藏的更深了……如何解决这个问题出现上面这个问题的本质原因是:“副作用”太多了,可以算作“副作用”的东西太多了。这导致useEffect被滥用。因此,要解决滥用问题,有必要针对不同类型的“副作用”提供官方解决方案。这样,如果具体的问题有具体的解决方案,useEffect就不会仓促了。从一个PR上看变化最近,React有一个很不起眼的PR[1]:大致意思是:之前,当你在一个未挂载的组件(unmounted)中调用setState时,会触发一个warning,这个PR会去掉这个warning。例如下面的代码在组件挂载时注册了handleChange:useEffect(()=>{functionhandleChange(){setState(store.getState())}store.subscribe(handleChange))},[])如果忘记写这行解绑代码:return()=>store.unsubscribe(handleChange)那么handleChange也可能在组件卸载后调用,然后调用setState。这是潜在的内存泄漏。在之前的React中,这种行为会报警告。那为什么要把这种行为下的warning去掉呢?PR背后的一方面,这个警告有一定的误判概率,比如“点击按钮提交表单”:asyncfunctionhandleSubmit(){setPending(true)awaitpost('/someapi')setPending(false)}之后点击按钮,调用setPending触发加载图标显示,然后发起post请求。有可能在返回请求之前卸载了组件。此时调用:setPending(false)不会造成内存泄漏风险,但会报警告。不过,去除警告还有一个更本质的原因:在第一个例子中,我们在useEffect中调用了store.subscribe,这个行为可以归类为:在组件中订阅外部源什么是“外部源”??任何“改变或不受React控制的来源”都是“外部来源”。例如:各种第三方状态管理库希望location.hash的变化会触发组件更新。将来,所有这些行为都会汇聚到HookuseMutableSource中。比如对于I/O操作(比如“请求数据”),大家都会放到useEffect中,以后结合Suspense使用resource可能是更好的选择:constresource=fetchDetail();functionPage(){return({data.name}
;}在上面的例子中,调用fetchDetail会发起一个数据请求。Details组件调用resource.read直接消费数据。如果数据尚未返回,视图将呈现最新的Suspense回退(即Loading...
)。综上所述,“副作用”有很多种。之前没办法,只能用useEffect。随着React18的稳定,针对不同的“副作用”场景会有更清晰的解决方案。到时候,或许终于迎来了胡克斯的真香时代……
