Reactsuspense详解
React.suspense是大家很少用到的一个功能。2018年16.6.0版本发布,其相关用法有的比较成熟,有的则比较不稳定,甚至还进行了更名和删除。下面我们来看看它的主要使用场景。1.Suspense配合lazy实现代码拆分import是webpack中的一种代码拆分用法,但是导入的文件返回一个promise,必须封装后才能使用,比如react-loadable函数Loadable的封装方法(opts){const{loading:LoadingComponent,loader}=optsreturnclassLoadableComponentextendsReact.Component{constructor(props){super(props)this.state={loading:true,//loading:null//模块loaded}}componentDidMount(){loader().then((loaded)=>{this.setState({loading:false,loaded})}).catch(()=>{})}render(){const{loading,loaded}=this.stateif(loading){return}elseif(loaded){//加载默认组件constLoadedComponent=loaded.__esModule?loaded.default:已加载;返回}else{返回空值;}}}}在promise返回后更新组件,如果使用suspense改写react-loadable,将会更优雅constProfilePage=React.lazy(()=>import('./ProfilePage'));}>2.1请求数据时解决loading问题letstatus="pending";letresult;constdata=newPromise(resolve=>setTimeout(()=>resolve("结果"),1000));functionwrapPromise(promise){letsuspender=promise.then(r=>{status="success";result=r;},e=>{status="error";result=e;});if(status==="pending"){抛出吊杆;}elseif(status==="error"){抛出结果;}elseif(status==="success"){返回结果;}}functionApp(){conststate=wrapPromise(data);return(
{state}
);}functionLoading(){return
..loading
}classTodoAppextendsReact.Component{render(){return(
正在加载>}>)}}ReactDOM.render(
,document.querySelector("#app"))这里的源码比较奇怪,组件App中请求数据状态时,在开头,返回的是throwpromise,就是让悬念捕捉到错误返回给加载组件。以上写法与悬念的实现有关。classSuspenseextendsReact.Component{state={promise:null}componentDidCatch(e){if(einstanceofPromise){this.setState({promise:e},()=>{e.then(()=>{this.setState({promise:null})})})}}render(){const{fallback,children}=this.propsconst{promise}=this.statereturn<>{promise?fallback:children}>}}从suspense的源码可以看出,suspense捕获到错误后,会进行监听,当返回值设置的时候,将loading改成children中的一个组件,但是这个会触发另一个组件渲染,所以需要缓存请求结果,最后变成上面的写法。这里有一个官方例子供参考。传送门2.2使用react-cache来缓存。上面的例子很反人类。实际项目中基本不可能这样写。使用react-cacheimportReact,{Suspense}from"react";import{unstable_createResourceascreateResource}from"react-cache";constmockApi=()=>{returnnewPromise((resolve,reject)=>{setTimeout(()=>resolve("Hello"),1000);});};constresource=createResource(mockApi);const问候语=()=>{constresult=resource.read();返回
{result}世界
;};constSuspenseDemo=()=>{return(
loading... }>
);};导出默认的SuspenseDemo;react-cache目前在线上不推荐使用上面项目中3.配合ConcurrentMode解决loadingflash问题。loading闪退的问题主要是API接口时间短,应该不会出现loading。需要在不考虑悬念的情况下判断接口速度。按照通常的写法,consttimeout=ms可以这样实现=>newPromise((_,r)=>setTimeout(r,ms));constrq=(api,ms,resolve,reject)=>async(...args)=>{constrequest=api(...args);se.race([request,timeout(ms)]).then(resolve,err=>{reject(err);returnrequest.then(resolve);});};suspense为我们提供了maxDuration属性,用来控制加载的触发时间importReactfrom"react";importReactDOMfrom"react-dom";const{unstable_ConcurrentMode:ConcurrentMode,Suspense,}=React;const{unstable_createRoot:createRoot}=ReactDOM;letstatus="pending";let结果;constdata=newPromise(resolve=>setTimeout(()=>resolve("结果"),3000));functionwrapPromise(promise){letsuspender=promise.then(r=>{status="success";结果=r;},e=>{状态="错误";结果=e;});if(status==="pending"){抛出吊杆;}elseif(status==="error"){抛出结果;}elseif(status==="success"){返回结果;}}functionTest(){conststate=wrapPromise(data);return(
{state}
);}functionLoading(){返回
..loading
}classTodoAppextendsReact.Component{render(){return(
}maxDuration={500}>)}}constrootElement=document.getElementById("root");createRoot(rootElement).render();上例源地址使用16.8.0版本。示例中使用了unstable_ConcurrentMode和unstable_createRoot语法,unstable_createRoot在16.11.0改名为createRoot,unstable_ConcurrentMode在16.9.0改名为unstable_createRoot。在最新的16.13.1测试中,发现ReactDOM.createRoot不存在,所以本例仅在16.8.0测试。总结以上就是关于suspense的所有场景,目前api不稳定,慎用。最近被字节跳动前端紧急招聘。有意者私信我,或发邮件574745389@qq.com前端基地上海、北京、南京、深圳、杭州,职位要求可参考https://job.toutiao.com/s/7wokvh除了前端,其他职位也欢迎发帖