【React中最优雅的异步请求】

给大家分享在React19中使用use+Suspense处理异步请求为什么是被认为最优雅的解决方案

一. 传统方案

解决异步请求的方案中,我们要处理至少两个最基本的逻辑

  1. 正常的数据显示
  2. 数据加载的UI状态

例如:

jsx 复制代码
export default function Index(){
  const [content, update] = useState({value: ''})
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    api().then(res => {
      setLoading(false)
      update(res)
    })
  }, []);
  
  if (loading) {
    return <Skeleton/>
  }
  
  return (
    <Message message={content.value}/>
  )
}

很明显,每个页面都这样干的话,会比较繁琐。因此,通常会通过自定义hook的方式封装请求逻辑简化每个页面的代码

jsx 复制代码
function useFetch() {
  const [content, update] = useState({value: ''})
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    api().then(res => {
      setLoading(false)
      update(res)
    })
  }, []);
    
  return {content,loading}
}

这样,页面代码就变成了如下更简洁的形式

jsx 复制代码
function index2() {
  const {content, loading} = useFetch()

  if(loading) {
    return <Skeleton/>
  }
  
  return (
    <Message message={content.value}/>
  )
}

✅ 常用的ahooks、useQuery等,都是这个封装思路

在UI层面,我们还可以做一层封装,把loading封装到UI组件逻辑中去。常见的使用方式可以是这样

jsx 复制代码
function Index2() {
  const {content, loading} = useFetch()

  return (
    <Message
      message={content.value}
      loading={loading}
    />
  )
}

也可以参考antd中,Spin的使用方式

jsx 复制代码
function Index2() {
  const {content, loading} = useFetch()

  return (
    <Spin tip="Loading...">
      <Message
        message={content.value}
      />
    </Spin>
  )
}

通过这样的两步优化让我们页面代码变得非常简洁。这也是日常使用最多的方式,开发效率也非常高。

但随着使用经验的增加,也处理了更多的场景,几乎绝大多数场景都能够平滑的应对,但这种方式依旧存在一些小小的痛点。

当在思考如何封装usefetch 时,首先会考虑清楚在众多场景之下,有哪些东西是变化量。变化的内容我们将其设计为参数传入

jsx 复制代码
function useFetch(params) {
  
}

常见的变化量包括:入参不同请求方式不同返回类型不同部分场景需要初始的默认值部分场景的接口并不需要立即请求返回结果可能需要二次处理才能正常使用参数变化之后的处理逻辑不同...

当不同的东西开始变得越来越多,优雅也在逐渐消失...

✅ 当前这肯定有对应的成套架构解决方案,到那时对于普通开发者来说变得有点难度,需要更丰富的经验来支撑才能应对各种不同的场景。

二. React 19的新方案

React19提出了一个新的方式,让我们应对这些复杂场景变得更加简单。那就是use + promise + Suspense

首先会把数据存储在promise中。然后promise定义为state

jsx 复制代码
const _api3 = (params) => {
  return new Promise(resolve => {
    resolve({ value: 'React does not preserve any state for renders that got suspended before they were able to mount for the first time. When the component has loaded, React will retry rendering the suspended tree from scratch.' })
  })
}
const [promise, setPromise] = useState(_api3)

如果有默认参数需要传入,只需要在执行 _api3 时传入参数即可

jsx 复制代码
const promise = useState(() => _api3({value: 10}))

如果我们在点击时,需要修改参数并且重新请求接口,可以一样重新执行_api3即可

jsx 复制代码
function clickHandler(){
  _api3({value: 20})
}

由于触发UI更新必须借助state的变化,因此每次将_api3 执行返回的promise存储在useState中,点击时,_api3的执行结果必定是新的promise对象,因此,代码更改为如下,即可触发UI的更新

jsx 复制代码
function clickHandler(){
  setPromise(_api3({value: 20}))
}

然后,将promise传入到具体的UI组件中去,并使用 Suspense 包裹起来

jsx 复制代码
export default function Index() {
  const [promise, setPromise] = useState(_api3)
  return (
    <Suspense fallback={<Skeleton />}>
      <Message promise={promise} />
    </Suspense>
  )
}

然后在 UI 组件内部,使用 use 获取 promise 中的数据即可

jsx 复制代码
const Message = (props) => {
  const content = use(props.promise)
  return (
    <div className='flex border border-blue-100 p-4 rounded-md shadow'>
      ...
    </div>
  )
}

在这套解决方案之下,参数的多变性处理起来就变得非常容易,可以直接控制参数是否变化,也可以直接控制接口是否需要重新请求。

只需要按照需求,在响应实践中执行对应的逻辑就可以了,而不需要像上面那种方案一样,还要额外封装,否则代码会变得更乱

jsx 复制代码
function clickHandler() {
setPromise(_api({value: 20}))
}

认真体会这段代码的优越性,可以非常自由的在不同的场景处理参数。例如,有的地方可能需要缓存上一次的参数,但是有的地方不需要。那么需要缓存的场景,可以随便单独缓存即可。

也不用受限于参数的变化是否会引发接口的重新请求,这里参数的变化与接口的执行被解耦开,直接由我们开发时控制

三. 总结

很显然,react19 中提到的解决异步逻辑的方案,是目前为止,被认为是最优雅的方案。这种方案不需要我们再进一步二次封装,就能够轻松应对各种复杂的场景。这必将成为未来开发的主流方案。

相关推荐
汝生淮南吾在北3 小时前
SpringBoot+Vue饭店点餐管理系统
java·vue.js·spring boot·毕业设计·毕设
酒尘&6 小时前
JS数组不止Array!索引集合类全面解析
开发语言·前端·javascript·学习·js
用户47949283569158 小时前
"讲讲原型链" —— 面试官最爱问的 JavaScript 基础
前端·javascript·面试
用户47949283569158 小时前
2025 年 TC39 都在忙什么?Import Bytes、Iterator Chunking 来了
前端·javascript·面试
JIngJaneIL8 小时前
基于Java非遗传承文化管理系统(源码+数据库+文档)
java·开发语言·数据库·vue.js·spring boot
+VX:Fegn08958 小时前
计算机毕业设计|基于springboot + vue心理健康管理系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
2401_860319528 小时前
在React Native鸿蒙跨平台开发中实现 二叉搜索树,如何实现一些基本的遍历方法,如中序遍历,中序遍历按顺序访问左子树、根节点、右子树
react native·react.js·harmonyos
大怪v9 小时前
【Virtual World 04】我们的目标,无限宇宙!!
前端·javascript·代码规范
蓝瑟12 小时前
告别重复造轮子!业务组件多场景复用实战指南
前端·javascript·设计模式
老华带你飞12 小时前
旅游|基于Java旅游信息系统(源码+数据库+文档)
java·开发语言·数据库·vue.js·spring boot·旅游