官方答:在React18中请求数据的正确姿势(其他框架也适用)

开发 前端
本文我们聊聊React18之后「不推荐的请求数据的方式」以及「推荐的请求数据」的方式。

大家好,我卡颂。

一些同学喜欢在useEffect中请求初始数据,类似这样:

useEffect(() => {
fetch(xxx).then(data => setState(data.json()))
}, [])

但​​React18​​并不推荐这种方式。

这么写有什么问题?如果不推荐这种方式,那么推荐的方式是什么呢?

本文来看看​​Dan​​在reddit[1]是如何回答上述问题的。

这是一个普遍的问题

除了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获取初始数据渲染,那么整个渲染流程如下:

  1. 父组件mount。
  2. 父组件useEffect执行,请求数据。
  3. 数据返回后重新渲染父组件。
  4. 子组件mount。
  5. 子组件useEffect执行,请求数据。
  6. 数据返回后重新渲染子组件。

可见,当父组件数据请求成功后子组件甚至还没开始首屏渲染。

这就是渲染中的瀑布问题 —— 数据像瀑布一样一级一级向下流动,流到的组件才开始渲染,很低效。

既然直接写useEffect有这么多问题,那么推荐的方式是什么呢?

推荐的方式

在Meta公司内部,基于Relay驱动数据(但请求数据要求使用GraphQL),所以这套架构比较难在社区普及开。

但是,现在社区已经有了成熟的「请求数据的方案」。

对于SSR,可以使用Next.js、Remix接管数据请求。

对于CSR,可以使用React Query、useSWR接管数据请求。

这些成熟的方案都致力于解决上述提到的问题。

如果不想使用这些方案,想自己写,可以参考React新文档中下面两篇文章:

  • 使用effect同步数据[2]
  • 你可能不需要使用effect[3]

想看中文的同学,可以看我写的总结 ——​​ React新文档:不要滥用effect哦!​

总结

本文我们聊了React18之后「不推荐的请求数据的方式」以及「推荐的请求数据」的方式。

其中「不推荐的请求数据的方式」不仅存在于React中,很多前端框架都有这样的问题。

参考资料

[1]reddit:https://www.reddit.com/r/reactjs/comments/vi6q6f/what_is_the_recommended_way_to_load_data_for/。

[2]使用effect同步数据: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。

责任编辑:姜华 来源: 魔术师卡颂
相关推荐

2021-06-16 06:05:25

React18React

2021-11-01 19:49:55

React组件模式

2021-06-22 07:45:57

React18startTransiReact

2023-03-21 08:31:13

ReconcilerFiber架构

2022-03-16 17:01:35

React18并发的React组件render

2022-05-16 08:00:55

ReactReact 18数组

2021-06-22 07:30:07

React18Automatic b自动批处理

2021-11-05 10:36:19

性能优化实践

2022-04-27 07:37:42

ReactReact18

2022-03-30 14:22:55

ReactReact18并发特性

2023-03-28 07:59:57

ReactReconciler

2019-12-27 15:58:57

大数据IT互联网

2016-12-12 08:48:24

2021-11-29 06:05:31

React组件前端

2022-03-25 08:31:09

ReactReact 18升级

2024-09-09 11:11:45

2021-04-12 05:55:29

缓存数据Axios

2018-01-11 15:31:39

命令Linux关机

2019-08-21 09:41:48

程序员JavaGitHub

2017-02-23 15:37:44

OptionObject容器
点赞
收藏

51CTO技术栈公众号