官方答:在React18中請(qǐng)求數(shù)據(jù)的正確姿勢(shì)(其他框架也適用)
大家好,我卡頌。
一些同學(xué)喜歡在useEffect中請(qǐng)求初始數(shù)據(jù),類似這樣:
useEffect(() => {
fetch(xxx).then(data => setState(data.json()))
}, [])
但React18并不推薦這種方式。
這么寫有什么問(wèn)題?如果不推薦這種方式,那么推薦的方式是什么呢?
本文來(lái)看看Dan在reddit[1]是如何回答上述問(wèn)題的。
這是一個(gè)普遍的問(wèn)題
除了React外,大部分以「組件」形式組織的前端框架,都有如下類似的API:
effect
didMount/didUpdate
如果有「初始化時(shí)請(qǐng)求數(shù)據(jù)」的需求,這類框架都會(huì)選擇在上述回調(diào)函數(shù)內(nèi)執(zhí)行請(qǐng)求操作,并在數(shù)據(jù)返回后更新狀態(tài)。
所以,這并不是React獨(dú)有的問(wèn)題。相反,他很普遍。
之所以在React中這么突出,是因?yàn)镽eact官方在引導(dǎo)開發(fā)者不要用這種形式書寫代碼(通過(guò)「嚴(yán)格模式下useEffect執(zhí)行兩次」放大這個(gè)問(wèn)題)。
而React之所以這么做,是為了項(xiàng)目的「性能」以及「UX」(User Experience,用戶體驗(yàn))。
下面我們來(lái)細(xì)聊這么做的影響。注意,這些影響同樣適用于其他框架。
為什么不推薦這么寫?
需要解決競(jìng)態(tài)問(wèn)題
在useEffect中請(qǐng)求數(shù)據(jù)要面臨的第一個(gè)問(wèn)題是「需要解決競(jìng)態(tài)問(wèn)題」。
假設(shè)你有個(gè)組件User,接收userID作為props,用userID請(qǐng)求數(shù)據(jù)后展示用戶信息。
下面是你的寫法:
function User({userID}) {
const [data, setData] = useState(null);
useEffect(() => {
const res = await fetch(`https://xxx/${userID}/`);
setData(res.json());
}, [userID]);
if (data) {
return <div>{data.name}</div>;
}
return null;
}
這里有個(gè)開發(fā)階段很難復(fù)現(xiàn)的bug —— 如果userID變化足夠快,會(huì)發(fā)起多個(gè)不同的用戶請(qǐng)求。
而最終展示哪個(gè)用戶的數(shù)據(jù),取決于哪個(gè)請(qǐng)求先返回。這就是「請(qǐng)求的競(jìng)態(tài)問(wèn)題」。
點(diǎn)擊返回按鈕后重新請(qǐng)求數(shù)據(jù)
如果用戶跳轉(zhuǎn)到新的頁(yè)面后,又通過(guò)瀏覽器回退按鈕回到當(dāng)前頁(yè)面,并不能立刻看到他跳轉(zhuǎn)前的頁(yè)面。
相反,看到的可能是個(gè)白屏 —— 因?yàn)檫€需要重新執(zhí)行useEffect獲取初始數(shù)據(jù)。
這個(gè)問(wèn)題的本質(zhì)原因是:沒有初始數(shù)據(jù)的緩存。
CSR時(shí)的白屏?xí)r間
CSR(Client-Side Rendering,客戶端渲染)時(shí)在useEffect中請(qǐng)求數(shù)據(jù),在數(shù)據(jù)返回前頁(yè)面都是白屏狀態(tài)。
瀑布問(wèn)題
如果父子組件都依賴useEffect獲取初始數(shù)據(jù)渲染,那么整個(gè)渲染流程如下:
父組件mount
父組件useEffect執(zhí)行,請(qǐng)求數(shù)據(jù)
數(shù)據(jù)返回后重新渲染父組件
子組件mount
子組件useEffect執(zhí)行,請(qǐng)求數(shù)據(jù)
數(shù)據(jù)返回后重新渲染子組件
可見,當(dāng)父組件數(shù)據(jù)請(qǐng)求成功后子組件甚至還沒開始首屏渲染。
這就是渲染中的瀑布問(wèn)題 —— 數(shù)據(jù)像瀑布一樣一級(jí)一級(jí)向下流動(dòng),流到的組件才開始渲染,很低效。
既然直接寫useEffect有這么多問(wèn)題,那么推薦的方式是什么呢?
推薦的方式
在Meta公司內(nèi)部,基于Relay驅(qū)動(dòng)數(shù)據(jù)(但請(qǐng)求數(shù)據(jù)要求使用GraphQL),所以這套架構(gòu)比較難在社區(qū)普及開。
但是,現(xiàn)在社區(qū)已經(jīng)有了成熟的「請(qǐng)求數(shù)據(jù)的方案」。
對(duì)于SSR,可以使用Next.js、Remix接管數(shù)據(jù)請(qǐng)求。
對(duì)于CSR,可以使用React Query、useSWR接管數(shù)據(jù)請(qǐng)求。
這些成熟的方案都致力于解決上述提到的問(wèn)題。
如果不想使用這些方案,想自己寫,可以參考React新文檔中下面兩篇文章:
使用effect同步數(shù)據(jù)[2]
你可能不需要使用effect[3]
想看中文的同學(xué),可以看我寫的總結(jié) —— React新文檔:不要濫用effect哦 原創(chuàng)
總結(jié)
本文我們聊了React18之后「不推薦的請(qǐng)求數(shù)據(jù)的方式」以及「推薦的請(qǐng)求數(shù)據(jù)」的方式。
其中「不推薦的請(qǐng)求數(shù)據(jù)的方式」不僅存在于React中,很多前端框架都有這樣的問(wèn)題。
參考資料
[1]
reddit:
https://www.reddit.com/r/reactjs/comments/vi6q6f/what_is_the_recommended_way_to_load_data_for/
[2]
使用effect同步數(shù)據(jù):
https://beta.reactjs.org/learn/synchronizing-with-effects#fetching-data
[3]
你可能不需要使用effect:
https://beta.reactjs.org/learn/you-might-not-need-an-effect#fetching-data
作者:卡頌
歡迎關(guān)注微信公眾號(hào) :魔術(shù)師卡頌