React18升级指南
时间:2023-03-14 12:05:04
科技观察
{visible&&}
但是React18中会加入unstable_batchedUpdates继续保留整个版本,因为很多开源库都用到了.卸载的组件更新状态警告我们在正常开发过程中不可避免地会犯以下错误:这个警告被广泛误解并且有点误导。原本打算用于以下场景:useEffect(()=>{functionhandleChange(){setState(store.getState());}store.subscribe(handleChange);return()=>store.unsubscribe(handleChange);},[]);如果你忘记调用unsubscribeineffectcleanup,你就会有内存泄漏。在实践中,上述情况并不常见。这在我们的代码中更为常见:asyncfunctionhandleSubmit(){setLoading(true);//组件可能会在我们等待时卸载awaitpost('/some-api');setLoading(false);}在这里,也会触发警告。但是,在这种情况下,警告具有误导性。这里没有实际的内存泄漏,Promise很快就解决了,之后它可以被垃圾收集。为了抑制这个警告,我们可能会写很多无用的isMounted判断,这会让代码变得更复杂。React18中移除了这个警告。组件返回null在React17中,如果组件在render中返回undefined,React会在运行时抛出一个错误:functionDemo(){returnundefined;}这里我们可以将undefined替换为null,程序将继续运行。此行为的目的是帮助用户发现不小心忘记返回语句的常见问题。对于React18的Suspensefallback,会出现undefined而不是错误,导致不一致。类型系统和Eslint现在都非常健壮,可以避免此类低级错误,因此React18不再检查因返回undefined导致的崩溃。StrictMode从React17开始,React会自动修改控制台方法,例如console.log()以在第二次调用生命周期函数时静默日志。但是,在某些可能有解决方法的情况下,它可能会导致不良行为。此行为已在React18中删除,如果安装了ReactDevTools>4.18.0,则第二次渲染期间的日志现在将以柔和的颜色显示在控制台中。新APIuseSyncExternalStoreuseSyncExternalStore进行了修改,由unstable_useMutableSource改为订阅外部数据源。主要帮助有外部存储需求的开发者解决撕裂问题。监听innerWidth变化的钩子最简单的例子:import{useMemo,useSyncExternalStore}from'react';functionuseInnerWidth():number{//保持subscribe固定引用,避免重复执行resizelistenerconst[subscribe,getSnapshot]=useMemo(()=>{return[(notify:()=>void)=>{//这里会使用Throttlewindow.addEventListener('resize',notify);return()=>{window.removeEventListener('resize',notify);};},//返回resize()之后需要的快照=>window.innerWidth,];},[]);返回useSyncExternalStore(subscribe,getSnapshot);}functionWindowInnerWidthExample(){constwidth=useInnerWidth();return
width:{width}
;}演示地址:https://codesandbox.io/s/usesyncexternalstore-demo-q47kyn。React自身的state已经原生解决了并发特性下的撕裂问题。useSyncExternalStore主要是针对框架开发者,比如redux,在控制state的时候可能不会直接使用React的state,而是在外部维护一个store对象,脱离React的管理,所以不能依赖React自动解析撕裂的问题。所以React对外提供了这样一个API。目前React-Redux8.0已经基于useSyncExternalStore实现。useInsertionEffectuseInsertionEffect的工作原理与useLayoutEffect大致相同,只是此时无法访问对DOM节点的引用。所以推荐的解决方案是使用这个Hook来插入样式表(或者如果你需要删除它们则引用它们):添加(规则);document.head.appendChild(getStyleForRule(规则));}});返回规则;}functionComponent(){letclassName=useCSS(rule);return
;}useIduseId是一种API,用于在客户端和服务器上生成唯一ID,同时避免水合作用不匹配。用法示例:functionCheckbox(){constid=useId();return(
选择框
);}并发(concurrent)模式并发模式是React的一组新特性,可以帮助应用程序保持响应并根据用户的设备性能和网络速度进行适当调整。可中断以修复阻塞渲染限制。在Concurrent模式下,React可以同时更新多个状态。通常,当我们更新状态时,我们希望这些更改立即反映在屏幕上。期望应用程序持续响应用户输入是合理的。但是,有时我们希望更新会延迟对屏幕的响应。以前很难在React中实现此功能。并发模式提供了一系列新工具来实现这一点。Transition在React18中引入了一个新的APIstartTransition,主要是为了在大量任务下保持UI响应。这个新的API可以通过将特定更新标记为“过渡”来显着改善用户交互。概述:import{startTransition}from'react';//紧急:显示输入内容setInputValue(input);//将回调函数中的更新标记为非紧急更新startTransition(()=>{setSearchQuery(input);});简单来说就是将startTransition回调包裹的setState触发的渲染标记为非紧急渲染,这些渲染可能会被其他紧急渲染抢占。一般来说,我们需要通知用户后台正在运行。为此提供一个带有isPending转换标志的useTransition,React将在状态转换期间提供视觉反馈,并在转换发生时保持浏览器响应。从“反应”导入{useTransition};const[isPending,startTransition]=useTransition();当转换挂起时,isPending值为true,此时可以在页面中放置一个加载器。正常情况下:使用useTransition性能:Demo地址:https://codesandbox.io/s/starttransition-demo-o59ld2。我们可以使用startTransition来包装我们想要移动到后台的任何更新。通常,这些类型的更新分为两类:渲染缓慢:这些更新需要时间,因为React需要做大量工作来转换UI以显示结果。网络慢:这些更新需要时间,因为React正在等待来自网络的一些数据。这种方法与Suspense紧密集成。网速慢的场景:一个列表页面,当我们点击“下一页”时,已有的列表立即消失,然后我们看到整个页面只有一个加载提示。可以说这是一种“不受欢迎”的加载状态。如果我们可以“跳过”这个过程并等到内容加载后再过渡到新页面,那就更好了。这里结合Suspense进行加载边界处理:constmockResource=fetchMockData(1);exportdefaultfunctionDemoList(){const[resource,setResource]=useState(mockResource);const[isPending,startTransition]=useTransition();返回((2));})}>下一页{isPending&&
加载中