官方答(在React18中请求数据的正确姿势(其他框架也适用))
【官方答(在React18中请求数据的正确姿势(其他框架也适用))】大家好,我卡颂。
一些同学喜欢在useEffect
中请求初始数据,类似这样:
useEffect(() => {
fetch(xxx).then(data => setState(data.json()))
}, [])
但
React18
并不推荐这种方式。这么写有什么问题?如果不推荐这种方式,那么推荐的方式是什么呢?
本文来看看
Dan
在reddit是如何回答上述问题的。欢迎加入人类高质量前端框架群,带飞
这是一个普遍的问题 除了
React
外,大部分以组件形式组织的前端框架,都有如下类似的API
:effect
didMount
/didUpdate
所以,这并不是
React
独有的问题。相反,他很普遍。之所以在
React
中这么突出,是因为React
官方在引导开发者不要用这种形式书写代码(通过严格模式下useEffect执行两次放大这个问题)。而
React
之所以这么做,是为了项目的性能以及UX(User Experience,用户体验)。下面我们来细聊这么做的影响。注意,这些影响同样适用于其他框架。
为什么不推荐这么写? 需要解决竞态问题
在
useEffect
中请求数据要面临的第一个问题是需要解决竞态问题。假设你有个组件
User
,接收userID
作为props
,用userID
请求数据后展示用户信息。下面是你的写法:
function User({userID}) {
const [data, setData] = useState(null);
useEffect(() => {
const res = await fetch(`https://xxx/${userID}/`);
setData(res.json());
}, [userID]);
if (data) {
return {data.name};
}
return null;
}
这里有个开发阶段很难复现的
bug
—— 如果userID
变化足够快,会发起多个不同的用户请求。而最终展示哪个用户的数据,取决于哪个请求先返回。这就是请求的竞态问题。
点击返回按钮后重新请求数据
如果用户跳转到新的页面后,又通过浏览器回退按钮回到当前页面,并不能立刻看到他跳转前的页面。
相反,看到的可能是个白屏 —— 因为还需要重新执行
useEffect
获取初始数据。这个问题的本质原因是:没有初始数据的缓存。
CSR时的白屏时间
CSR
(Client-Side Rendering,客户端渲染)时在useEffect
中请求数据,在数据返回前页面都是白屏状态。瀑布问题
如果父子组件都依赖
useEffect
获取初始数据渲染,那么整个渲染流程如下:- 父组件
mount
- 父组件
useEffect
执行,请求数据 - 数据返回后重新渲染父组件
- 子组件
mount
- 子组件
useEffect
执行,请求数据 - 数据返回后重新渲染子组件
这就是渲染中的瀑布问题 —— 数据像瀑布一样一级一级向下流动,流到的组件才开始渲染,很低效。
文章图片
既然直接写
useEffect
有这么多问题,那么推荐的方式是什么呢?推荐的方式
在
Meta
公司内部,基于Relay
驱动数据(但请求数据要求使用GraphQL
),所以这套架构比较难在社区普及开。但是,现在社区已经有了成熟的请求数据的方案。
对于
SSR
,可以使用Next.js
、Remix
接管数据请求。对于
CSR
,可以使用React Query
、useSWR
接管数据请求。这些成熟的方案都致力于解决上述提到的问题。
如果不想使用这些方案,想自己写,可以参考
React
新文档中下面两篇文章:- 使用effect同步数据
- 你可能不需要使用effect
总结 本文我们聊了
React18
之后不推荐的请求数据的方式以及推荐的请求数据的方式。其中不推荐的请求数据的方式不仅存在于
React
中,很多前端框架都有这样的问题。推荐阅读
- 《当幸福来敲门》相比大多国产片在影片最后炫扬主人公取得的成就的固有套路,我更喜欢这部影片的结尾
- 人在北方还活着
- 走在智能家居的前列佼佼者——火凰智能家居
- 京都的味道,蔓延在每一条街道
- 微信朋友圈
- 我在乎的是
- 写在2017年的第一天
- 《我的》(1-1)
- 哪有什么岁月静好,只不过是有人在替你负重前行
- 洗澡乐趣多