当前位置: 首页 > 科技观察

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

时间:2023-03-19 09:55:36 科技观察

大家好,我是凯森。有些同学喜欢在useEffect中请求初始数据,像这样:useEffect(()=>{fetch(xxx).then(data=>setState(data.json()))},[])但是React18这种方法不推荐。这样写有什么问题?如果这不是推荐的方式,那么推荐的方式是什么?本文来看看丹是如何在reddit[1]上回答上述问题的。这是一个常见问题。除了React,大多数以“组件”的形式组织起来的前端框架都有类似如下的API:effect。didMount/didUpdate。如果有“初始化时请求数据”的需求,此类框架会选择在上述回调函数中执行请求操作,并在数据返回后更新状态。所以,这不是React独有的问题。相反,他是普世的。之所以在React中如此突出,是因为React官方指导开发者不要写这种形式的代码(这个问题被“useEffect在严格模式下被执行了两次”放大了)。React之所以这样做,是为了项目的“性能”和“UX”(用户体验)。让我们详细谈谈这方面的影响。请注意,这些影响也适用于其他框架。为什么不建议这样写呢?需要解决竞态问题在useEffect中请求数据首先要面对的问题就是“需要解决竞态问题”。假设您有一个组件User,它接收userID作为props,并在使用userID请求数据后显示用户信息。写法如下:functionUser({userID}){const[data,setData]=useState(null);useEffect(()=>{constres=awaitfetch(`https://xxx/${userID}/`);setData(res.json());},[userID]);如果(数据){return

{data.name}
;}returnnull;}这里有一个开发阶段难以复现的bug——如果userID的变化足够快,就会发起多个不同的用户请求。最终显示哪个用户的数据取决于先返回哪个请求。这就是“请求竞争问题”。点击后退按钮后重新请求数据如果用户跳转到新页面,通过浏览器后退按钮返回当前页面,则无法立即看到跳转前的页面。相反,您看到的可能是白屏——因为您需要重新执行useEffect才能获取初始数据。这个问题的本质原因是:没有缓存初始数据。CSR期间的白屏时间CSR(Client-SideRendering,客户端渲染)在useEffect中请求数据时,在返回数据之前页面处于白屏状态。瀑布问题如果父子组件都依赖useEffect来获取初始数据渲染,那么整个渲染过程如下:父组件挂载。父组件useEffect执行并请求数据。返回数据后重新渲染父组件。子组件挂载。子组件useEffect执行并请求数据。返回数据后重新渲染子组件。可以看到,当父组件请求数据成功时,子组件甚至还没有开始渲染首屏。这就是渲染中的瀑布问题——数据像瀑布一样一层一层往下流,流向它的组件开始渲染,效率很低。既然直接写useEffect问题那么多,那有什么推荐的方式呢?推荐的方式是在Meta公司内部,基于Relay驱动数据(但是请求数据需要使用GraphQL),所以这种架构很难在社区普及。不过,现在社区已经有了成熟的“请求数据方案”。对于SSR,可以使用Next.js和Remix来接管数据请求。对于CSR,可以使用ReactQuery,使用SWR来接管数据请求。这些成熟的方案就是致力于解决上述问题。如果你不想使用这些解决方案,想自己写,可以参考新的React文档中的以下两篇文章:使用effect来同步数据[2]你可能不需要使用effect[3]为想看中文的同学可以看看我写的总结——NewReactdocs:Don'tabuseeffects!总结在这篇文章中,我们谈到了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]你可能不需要使用效果:https://beta.reactjs.org/learn/you-might-not-need-an-effect#fetching-data。