react在16.6推出了<Suspense>組件,看了官方文檔和一些分析文章,發(fā)現(xiàn)以后react的開發(fā)方式將會發(fā)生重大改變
Suspense功能
聲明方式等待任何內(nèi)容(組件),包括數(shù)據(jù),替代react-loadable做code spliting
Suspense 常見API
1.fallback:Suspense里面還有"任務"執(zhí)行時展示的UI
2.children: 一般是異步組件(配合lazy包裹的異步組件),為什么說一般是,因為官方?jīng)]提供穩(wěn)定的api來將取數(shù)邏輯跟Suspense配合出延遲效果,但我們可以hack
異步加載組件使用lazy(<MyComponent />)就一定要Suspense包裹,看了一些分析Suspense源碼的文章,了解到Suspense內(nèi)部是利用componentDidCatch來捕獲到lazy主動throw出來的Promise來做條件渲染,渲染fallback內(nèi)容,待lazy內(nèi)的Promise狀態(tài)resolve,完整內(nèi)容就渲染出來
// lazy內(nèi)部也是返回了Promise對象
const AsyncCompnentA = lazy(() => import('../componentA'))
export default () => {
return (
<Suspense fallback={<Loading />}>
<AsyncCompnentA />
</Suspense>
)
}
簡易理解中的Suspense源碼
class Suspense extends React.Component {
state = {
promise: null
}
componentDidCatch(e) {
if(e instanceof Promise) {
this.setState({
promise: e,
}, () => {
e.then(() => {
promise: null
})
})
}
}
render() {
return this.state.promise ? this.props.fallback : this.props.children
}
}
不難理解,通過componentDidCatch捕獲的異常改變自身state,然后憑借promise值做條件渲染
而官方?jīng)]提供取數(shù)延遲,貌似暫時只開放給請求庫(swr)而非大眾調(diào)用者,但分析了一波源碼,利用throw promise也是可以模擬的
const PromiseThrower = () => {
const getData = () => {
const getData = fetch()
getData.then(data => {
returnData = data
})
if (returnData === cache) {
throw getData
}
return returnData
}
return <>{getData()}</>
}
// useage
export default () => {
return (
<Suspense fallback={<Loading />}>
<PromiseThrower />
</Suspense>
)
}
以往取數(shù)組件邏輯
以往loading跟取數(shù)(網(wǎng)絡請求)完全耦合,三目渲染、樣板代碼寫到吐
const AsyncComponentC = () => {
const [loading, setLoading] = useState(false)
useEffect(() => {
setLoading(true)
fetch('xxx').then(() => {
setLoading(false)
})
}, [])
return loading ? <Loading /> : <RealComponent />
}
未來使用猜想
對于一些短命組件,實時性強且跟應用其它組件少聯(lián)系的組件,我們大可將數(shù)據(jù)流范圍從應用級(redux之類)收窄至組件本身state,這樣讓組件更加single,而我們自行封裝的取數(shù)邏輯(hook)可以在axios,fetch甚至原生xhr上二次封裝,在里面loading狀態(tài)時throw個promise就可以在<Supense>里有延遲效果了
// usage
const AsyncComponentA = () => {
const { data } = useFetch('api1') // 自己封裝取數(shù)邏輯
return (
<div>{data.whatthe}</div>
)
}
const AsyncComponentB = () => {
const { data } = useFetch('api2') // 自己封裝取數(shù)邏輯
return (
<div>{data.whatthe}</div>
)
}
<Suspense fallback={<Loading />}>
<AsyncComponentA />
<AsyncComponentB />
</Suspense>
Suspense和改造過的取數(shù)hook能帶來什么效果
1.loading邏輯和 UI 解耦
2.Suspense可以嵌套使用,聲明方式時加載狀態(tài)順序可控
3.競態(tài)更加可控
參考文獻
1.https://zh-hans.reactjs.org/docs/concurrent-mode-suspense.html#what-if-i-dont-use-relay
2.https://juejin.im/post/5c7d4a785188251b921f4e26#heading-5