当前位置:首页 > 科技  > 软件

震惊!用 Suspense 解决请求依赖的复杂场景居然这么简单!

来源: 责编: 时间:2024-06-11 17:52:06 71观看
导读有一种复杂场景 React 新手经常处理不好。那就是一个页面有多个模块,每个模块都有自己的数据需要请求。与此同时,可能部分模块的数据还要依赖父级的异步数据才能正常请求自己的数据。如下图所示,当我们直接访问该页面时,

有一种复杂场景 React 新手经常处理不好。TUD28资讯网——每日最新资讯28at.com

那就是一个页面有多个模块,每个模块都有自己的数据需要请求。与此同时,可能部分模块的数据还要依赖父级的异步数据才能正常请求自己的数据。如下图所示,当我们直接访问该页面时,页面请求的数据就非常多。而且这些数据还有一定的先后依赖关系。TUD28资讯网——每日最新资讯28at.com

TUD28资讯网——每日最新资讯28at.com

一、重新考虑初始化

和之前的方案一样,我们先定义父组件的请求接口。TUD28资讯网——每日最新资讯28at.com

const getMessage = async () => {  const res = await fetch('https://api.chucknorris.io/jokes/random')  return res.json()}

然后在父组件中,将 getMessage() 执行之后返回的 promise 作为状态存储在 useState 中。这样,当我点击时,只需要重新执行依次 getMessage() 就可以更新整个组件。TUD28资讯网——每日最新资讯28at.com

const [  messagePromise,   setMessagePromise] = useState(null)

但是此时我们发现,messagePromise 并没有初始值,因此初始化时,接口并不会请求。这种情况下,有两种交互我们需要探讨。一种是通过点击按钮来初始化接口。另外一种就是组件首次渲染就要初始化接口。TUD28资讯网——每日最新资讯28at.com

我们之前的案例中,使用了取巧的方式,在函数组件之外提前获取了数据,这会导致访问任何页面该数据都会加载,因此并非合适的手段。TUD28资讯网——每日最新资讯28at.com

// 我们之前的案例这样做是一种取巧的方式const api = getMessage()function Message() {  ...

但是如果我们直接把 getMessage() 放在组件内部执行,也存在不小的问题。因为当组件因为其他的状态发生变化需要重新执行时,此时 getMessage() 也会冗余的多次执行。TUD28资讯网——每日最新资讯28at.com

// 此时会冗余多次执行const [  messagePromise,   setMessagePromise] = useState(getMessage())

理想的情况是 getMessage() 只在组件首次渲染时执行依次,后续状态的改变就不在执行。而不需要多次执行。TUD28资讯网——每日最新资讯28at.com

我们先来考虑通过点击事件初始化接口的交互。此时我们可以先设置 messagePrmoise 的初始值为 null。TUD28资讯网——每日最新资讯28at.com

const [  messagePromise,   setMessagePromise] = useState(null)

不过这样做有一个小问题就是如果我将 messagePromise 值为 null 时传递给了子组件。那么子组件就会报错,因此我们需要特殊处理。一种方式就是在子组件内部判断。TUD28资讯网——每日最新资讯28at.com

const MessageOutput = ({messagePromise}) => {  if (!messagePromise) return  const messageContent = use(messagePromise)

或者:TUD28资讯网——每日最新资讯28at.com

// 这种写法是在需要默认显示状态时的方案const MessageOutput = ({messagePromise}) => {  const messageContent = messagePromise ? use(messagePromise) : {value: '默认值'}

另外一种思路就是设置一个状态,子组件基于该状态的值来是否显示。然后在点击时将其设置为 true。TUD28资讯网——每日最新资讯28at.com

const [show, setShow] = useState(false)function __clickHandler() {  setMessagePromise(getMessage())  setShow(true)}
{show && <MessageContainer messagePromise={messagePromise} />}

另外一种交互思路就是初始化时就需要马上请求数据。此时我们为了确保 getMessage() 只执行一次,可以新增一个非 state 状态来记录组件的初始化情况。默认值为 false,初始化之后设置为 true。TUD28资讯网——每日最新资讯28at.com

const i = useRef(false)let __api = i.current ? null : getMessage()const [  messagePromise,   setMessagePromise] = useState(null)

然后在 useEffect 中,将其设置为 true,表示组件已经初始化过了。TUD28资讯网——每日最新资讯28at.com

useEffect(() => {  i.current = true}, [])

这是利用 useState 的内部机制,初始化值只会赋值一次来做到的。从而我们可以放心更改后续 __api 的值为 null.TUD28资讯网——每日最新资讯28at.com

从这个细节的角度来说,函数组件多次执行的确会给开发带来一些困扰,Vue3/Solid 只执行一次的机制会更舒适一些,不过处理得当也能避免这个问题。TUD28资讯网——每日最新资讯28at.com

二、Suspense 嵌套

接下来,我们需要考虑的就是 Suspense 嵌套执行的问题就行了。这个执行起来非常简单。我们只需要将有异步请求的模块用 Suspense 包裹起来当成一个子组件。然后该子组件可以当成一个常规的子组件作为 Suspense 组件的子组件。TUD28资讯网——每日最新资讯28at.com

例如,我们声明一个子组件如下所示:TUD28资讯网——每日最新资讯28at.com

const getApi = async () => {  const res = await fetch('https://api.chucknorris.io/jokes/random')  return res.json()}export default function Index(props) {  const api = getApi()  return (    <div>      <div id='tips'>多个 Suspense 嵌套,子组件第一部分</div>      <div className="content">        <div className='_05_dou1_message'>父级消息: {props.value}</div>        <Suspense fallback={<div>Loading...</div>}>          <Item api={api} />        </Suspense>      </div>    </div>  )}const Item = ({api}) => {  const joke = api ? use(api) : {value: 'nothing'}  return (    <div className='_03_a_value_update'>子级消息:{joke.value}</div>  )}

然后我可以将这个子组件放在 Suspense 内就可以了。TUD28资讯网——每日最新资讯28at.com

import DouPlus1 from './Dou1'import DouPlus2 from './Dou2'
const MessageOutput = ({messagePromise}) => {  const messageContent = use(messagePromise)  return (    <div>      <p>{messageContent.value}</p>      <DouPlus1 value={messageContent.value} />      <DouPlus2 value={messageContent.value} />    </div>  )}

在另外一个子组件中,我们还设计了内部状态,用于实现切换按钮,来增加页面交互的复杂度。并且每次切换都会请求接口。TUD28资讯网——每日最新资讯28at.com

TUD28资讯网——每日最新资讯28at.com

如果切换时,上一个接口没有请求完成,React 会自己处理好数据的先后问题。不需要我们额外考虑竞态条件的情况。完整代码如下:TUD28资讯网——每日最新资讯28at.com

var tabs = ['首页', '视频', '探索']export default function Index() {  var r = useRef(false)  var api = r.current ? null : getApi()  const [promise, setPromise] = useState(api)  const [current, setCurrent] = useState(0)  useEffect(() => {    r.current = true  }, [])  return (    <div>      <div id='tips'>多个 Suspense 嵌套,子组件第二部分</div>      <div className="content">        {tabs.map((item, index) => (          <button             id='btn_05_item'             className={current == index ? 'active' : ''}            onClick={() => {              setCurrent(index)              setPromise(getApi())            }}            key={item}          >{item}</button>        ))}                <Suspense fallback={<div className='_05_a_value_item'>Loading...</div>}>          <Item api={promise} />        </Suspense>      </div>    </div>  )}const Item = ({api}) => {  const joke = use(api)  return (    <div className='_05_a_value_item'>{joke.value}</div>  )}

三、总结

当我们要在复杂交互的情况下使用嵌套 Suspense 来解决问题,如果我们组件划分得当、与数据依赖关系处理得当,那么代码就会相当简单。不过这对于开发者来说,会有另外一个层面的要求。那就是如何合理的处理好组件归属问题。TUD28资讯网——每日最新资讯28at.com

许多前端页面开发难度往往都是由于组件划分不合理,属性归属问题处理不够到位导致的。因此 Suspense 在这个层面有了一个刚需,开发者必须要具备合理划分组件的能力,否则即使使用了 Suspense,也依然可能导致页面一团混乱。TUD28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-93090-0.html震惊!用 Suspense 解决请求依赖的复杂场景居然这么简单!

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: C++ 首度超越 C 语言仅次榜首 Python,TIOBE 编程指数六月排行榜公布

下一篇: PHP 服务实现性能剖析、跟踪和可观察性14444444444444】=102102102102102102102102102102102102102102102102实践

标签:
  • 热门焦点
  • 天猫精灵Sound Pro体验:智能音箱没有音质?来听听我的

    天猫精灵Sound Pro体验:智能音箱没有音质?来听听我的

    这几年除了手机作为智能生活终端最主要的核心之外,第二个可以成为中心点的产品是什么?——是智能音箱。 手机在执行命令的时候有两种操作方式,手和智能语音助手,而智能音箱只
  • 6月iOS设备性能榜:M2稳居榜首 A系列只能等一手3nm来救

    6月iOS设备性能榜:M2稳居榜首 A系列只能等一手3nm来救

    没有新品发布,自然iOS设备性能榜的上榜设备就没有什么更替,仅仅只有跑分变化而产生的排名变动,毕竟苹果新品的发布节奏就是这样的,一年下来也就几个移动端新品,不会像安卓厂商,一
  • 8月总票房已突破10亿!《封神》第一:口碑已经成了

    8月总票房已突破10亿!《封神》第一:口碑已经成了

    8月5日消息,据灯塔专业版数据,截至8月5日9时35分,8月总票房(含预售)已突破10亿。其中,《封神》以大比分的优势领先。根据官方消息,目前该片总票房已经超过14.
  • 把LangChain跑起来的三个方法

    把LangChain跑起来的三个方法

    使用LangChain开发LLM应用时,需要机器进行GLM部署,好多同学第一步就被劝退了,那么如何绕过这个步骤先学习LLM模型的应用,对Langchain进行快速上手?本片讲解3个把LangChain跑起来
  • 一篇聊聊Go错误封装机制

    一篇聊聊Go错误封装机制

    %w 是用于错误包装(Error Wrapping)的格式化动词。它是用于 fmt.Errorf 和 fmt.Sprintf 函数中的一个特殊格式化动词,用于将一个错误(或其他可打印的值)包装在一个新的错误中。使
  • 企业采用CRM系统的11个好处

    企业采用CRM系统的11个好处

    客户关系管理(CRM)软件可以为企业提供很多的好处,从客户保留到提高生产力。  CRM软件用于企业收集客户互动,以改善客户体验和满意度。  CRM软件市场规模如今超过580
  • 一个注解实现接口幂等,这样才优雅!

    一个注解实现接口幂等,这样才优雅!

    场景码猿慢病云管理系统中其实高并发的场景不是很多,没有必要每个接口都去考虑并发高的场景,比如添加住院患者的这个接口,具体的业务代码就不贴了,业务伪代码如下:图片上述代码有
  • Android 14发布:首批适配机型公布

    Android 14发布:首批适配机型公布

    5月11日消息,谷歌在今天凌晨举行了I/O大会,本次发布会谷歌带来了自家的AI语言模型PaLM 2、谷歌Pixel Fold折叠屏、谷歌Pixel 7a手机,同时发布了Androi
  • 微软发布Windows 11新版 引入全新任务栏状态

    微软发布Windows 11新版 引入全新任务栏状态

    近日,微软发布了Windows 11新版,而Build 22563更新主要引入了几周前曝光的平板模式任务栏等,系统更流畅了。更新中,Windows 11加入了专门针对平板优化的任务栏
Top