目录
引言这是一个普遍的问题为什么不推荐这么写?
需要解决竞态问题点击返回按钮后重新请求数据CSR时的白屏时间瀑布问题推荐的方式
总结
引言
一些同学喜欢在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 <div>{data.name}</div>;
- }
- 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
想看中文的同学,可以看我写的总结 —— React新文档:不要滥用effect哦
总结
本文我们聊了React18之后
不推荐的请求数据的方式以及
推荐的请求数据的方式。
其中
不推荐的请求数据的方式不仅存在于React中,很多前端框架都有这样的问题。
以上就是React18中请求数据的官方姿势适用其他框架的详细内容,更多关于React18请求数据的资料请关注中国红客联盟其它相关文章!