自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

React Suspense 進(jìn)階用法,結(jié)合 useTransition 使用

開發(fā) 前端
useTransition 是 React 專門為并發(fā)模式提供的一個(gè)基礎(chǔ) hook,它能夠幫助我們?cè)诓蛔枞?UI 渲染的情況下更新狀態(tài)。意思就是說,我們可以借助 useTransition 將任務(wù)的優(yōu)先級(jí)調(diào)得比 I/O 的響應(yīng)低一些。

一、更舒適的交互

先來看一下我們想要實(shí)現(xiàn)的交互效果,如圖所示。

我們?cè)谇懊鎸W(xué)習(xí)了 Suspense。Suspense 的 fallback 與子組件內(nèi)容的顯示是一個(gè)互斥關(guān)系。因此,當(dāng)我們?cè)谡?qǐng)求過程中,需要顯示 Loading 時(shí),內(nèi)容就會(huì)被隱藏掉。

<Suspense fallback={<Loading />}>
  <Albums />
</Suspense>

之前我們實(shí)現(xiàn)的交互效果如下。

這種交互效果其實(shí)還可以,但是許多對(duì)交互有更高要求的團(tuán)隊(duì),不會(huì)接受這樣的頁面大幅度抖動(dòng)的交互。

例如在 antd 的 Table 組件中,它們選擇的方案是在列表組件上覆蓋了一個(gè) Loading,此時(shí)的 Loading 效果與列表并非是一個(gè)互斥關(guān)系。這樣的交互體驗(yàn)要比用 Loading 整體替換掉表格要好很多。

所以,在目前學(xué)習(xí)階段,我們面臨的一個(gè)困惑就是,在使用 Suspense + use 來完成功能的同時(shí),又如何優(yōu)雅的做到這種非互斥的交互效果呢?

我們想要的最佳交互效果氛圍兩個(gè)階段。

  •  初始化階段,渲染 Suspense fallback 對(duì)應(yīng)的組件。此時(shí)內(nèi)部還沒有辦法顯示,我們可以放置一個(gè) Loading 或者骨架屏組件。
  • 更新階段,我們希望阻止 fallback 的出現(xiàn)。直接在 Suspense 子組件內(nèi)部處理更新階段的 loading。這樣就可以確保更新階段,子組件內(nèi)容與更新 loading 共存。

但是以目前學(xué)習(xí)到的知識(shí)點(diǎn),肯定還做不到這樣的效果,因此我們要引入新的概念:useTransition。

二、useTransition 概念解讀

useTransition 是 React 專門為并發(fā)模式提供的一個(gè)基礎(chǔ) hook,它能夠幫助我們?cè)诓蛔枞?UI 渲染的情況下更新狀態(tài)。意思就是說,我們可以借助 useTransition 將任務(wù)的優(yōu)先級(jí)調(diào)得比 I/O 的響應(yīng)低一些。

const [isPending, startTransition] = useTransition()

useTransition 的調(diào)用不需要參數(shù),他的執(zhí)行返回兩個(gè)參數(shù)。

isPending:是否還存在等待處理的 transition,表示被降低優(yōu)先級(jí)的更新任務(wù)還沒有處理完成。

startTransition:標(biāo)記任務(wù)的優(yōu)先級(jí)為 transition,該優(yōu)先級(jí)低于正常任務(wù)更新。它的用法如下,我們會(huì)將更新任務(wù)在它的回調(diào)函數(shù)中執(zhí)行。

function TabContainer() {
  const [isPending, startTransition] = useTransition();
  const [tab, setTab] = useState('about');

  function selectTab(nextTab) {
    startTransition(() => {
      setTab(nextTab);
    });
  }
  // ……
}

在優(yōu)先級(jí)的排序中,被 startTransition 標(biāo)記的任務(wù)比 Suspense fallback 更高一些。因此,我們可以利用這個(gè)特性,來避免 fallback 的渲染,當(dāng) startTransition 標(biāo)記的任務(wù)執(zhí)行完成,請(qǐng)求已經(jīng)完成,此時(shí) fallback 也就得不到渲染的機(jī)會(huì)了。

這里需要注意的是,標(biāo)記的任務(wù)指的不是 setState ,而是對(duì)應(yīng)的 UI 渲染任務(wù),傳遞給 startTransition 的回調(diào)函數(shù)必須是同步函數(shù)。

我們可以正常這樣使用。

startTransition(() => {
  // ? 在調(diào)用 startTransition 中更新狀態(tài)
  setPage('/about');
});

但是不能在回調(diào)函數(shù)中使用異步調(diào)用。這樣會(huì)導(dǎo)致并發(fā)模式的任務(wù)排序出現(xiàn)問題。

startTransition(() => {
  // ? 在調(diào)用 startTransition 后更新狀態(tài)
  setTimeout(() => {
    setPage('/about');
  }, 1000);
});

但是,我們可以把 startTransition 傳遞給 setTimeout。

setTimeout(() => {
  startTransition(() => {
    // ? 在調(diào)用 startTransition 中更新狀態(tài)
    setPage('/about');
  });
}, 1000);
startTransition(async () => {
  await someAsyncFunction();
  // ? 在調(diào)用 startTransition 后更新狀態(tài)
  setPage('/about');
});
await someAsyncFunction();
startTransition(() => {
  // ? 在調(diào)用 startTransition 中更新狀態(tài)
  setPage('/about');
});

三、Suspense + useTransition

現(xiàn)在基于前面的知識(shí)點(diǎn),我們來著手解決我們自己案例中的交互體驗(yàn)的提升。先來回顧一下之前的代碼。

export default function Index() {
  const [api, setApi] = useState(getApi)

  function __clickToGetMessage() {
    setApi(getApi())
  }
  
  return (
    <div>
      <div id='tips'>點(diǎn)擊按鈕獲取一條新的數(shù)據(jù)</div>
      <button onClick={__clickToGetMessage} disabled={isPending}>獲取數(shù)據(jù)</button>

      <div className="content">
        <Suspense fallback={<div>loading...</div>}>
          <Item api={api} isPending={isPending} />
        </Suspense>
      </div>
    </div>
  )
}

在這個(gè)基礎(chǔ)之上,我們只需要引入 useTransition 的使用即可。

export default function Index() {
  const [api, setApi] = useState(getApi)
  const [isPending, startTransition] = useTransition()

  function __clickToGetMessage() {
    startTransition(() => {
      setApi(getApi())
    })
  }
  
  ...
}

setApi 所引發(fā)的任務(wù)更新被標(biāo)記為 transition,他的優(yōu)先級(jí)比 fallback 更高,因此此時(shí)我們需要等待 setApi 執(zhí)行完成。isPending 表示是否還有待處理的 transition 任務(wù),在這個(gè)案例中,他可以表示請(qǐng)求正在發(fā)生,作用與 loading 完全一致。

因此,我們可以將 isPending 傳遞給子組件,在子組件內(nèi)部通過 isPending 來設(shè)計(jì) loading UI。我們這里將組件的透明度調(diào)低。

<Item api={api} isPending={isPending} />
const Item = (props) => {
  const {isPending, api} = props
  const joke = use(api)
  return (
    <div 
      className='a_value' 
      style={{opacity: isPending ? 0.5 : 1}}
    >{joke.value}</div>
  )
}

最終的演示效果如下:

loading... 字樣的出現(xiàn)表示初始化時(shí)請(qǐng)求接口。整體變淺表示更新時(shí)請(qǐng)求接口。完整的達(dá)到了我們的訴求。

四、input 中的實(shí)時(shí)請(qǐng)求

我們可以利用同樣的方式,在搜索快速輸入時(shí)做到這個(gè)交互。每一個(gè)字符的變化,在之前的嘗試中,我們都會(huì)請(qǐng)求一次接口。因此之前的交互如下:

我們希望如果列表已經(jīng)顯示過一次,那么在搜索過程中,列表就顯示舊值,而不用切換到 fallback 的 Loading 組件。

代碼的調(diào)整與上面的案例幾乎一樣,如下:

export default function Index() {
  const [api, setApi] = useState(postApi)
  const [isPending, startTransition] = useTransition()

  function __inputChange() {
    startTransition(() => {
      api.cancel()
      setApi(postApi())
    })
  }
  ....

但是,我們注意觀察交互動(dòng)畫,當(dāng)我們輸入完之后,過了很長(zhǎng)一段時(shí)間,isPending 狀態(tài)才發(fā)生變化。也就是說,在這很長(zhǎng)的時(shí)間里,一直有 transition 任務(wù)在執(zhí)行。為什么會(huì)發(fā)生這種事情呢?然后我們觀察了一下 network 面板,發(fā)現(xiàn)每一個(gè)請(qǐng)求都發(fā)生了。取消請(qǐng)求的代碼并沒有生效。

這個(gè)時(shí)候我們?cè)诠俜轿臋n中看到,useTransition 并不會(huì)中斷網(wǎng)絡(luò)請(qǐng)求。目前我暫時(shí)也還沒有找到一個(gè)比較好的方式,在結(jié)合了 useTransition 的情況下去優(yōu)雅的取消請(qǐng)求。

希望評(píng)論區(qū)能出現(xiàn)大佬找到更好的方案。

因此,我選擇了使用防抖的思路來避免多次請(qǐng)求的發(fā)生。代碼改造如下:

const [api, setApi] = useState(postApi)
const [isPending, startTransition] = useTransition()
const timer = useRef(21)

function __inputChange() {
  clearTimeout(timer.current)
  timer.current = setTimeout(() => {
    startTransition(() => {
      api.cancel()
      setApi(postApi())
    })
  }, 300)  
}
...

最終交互效果如下:

當(dāng)然,在官方文檔中,也提到了,如果我們期望在交互過程中減少冗余請(qǐng)求的發(fā)生,我們可以繼續(xù)使用防抖/節(jié)流來解決問題。

責(zé)任編輯:姜華 來源: 這波能反殺
相關(guān)推薦

2022-02-09 07:25:34

SuspenseReact項(xiàng)目

2025-04-27 01:47:00

React數(shù)據(jù)集優(yōu)化

2023-11-01 11:27:42

ping命令網(wǎng)絡(luò)

2024-06-19 08:45:13

2021-06-07 08:41:59

React異步組件

2021-04-27 11:28:21

React.t事件元素

2024-09-20 08:14:16

2024-01-26 09:01:30

HooksReact 19版本

2022-08-15 17:34:22

react-routv6

2023-06-08 09:00:00

2020-09-14 10:16:45

React

2021-11-30 05:45:48

React組件前端

2021-02-01 11:01:18

Bash腳本Linux

2021-06-07 13:02:31

Shell腳本Linux

2023-11-29 13:51:00

2025-04-10 00:11:01

2024-12-17 09:00:00

lambda函數(shù)Python

2022-06-27 07:23:20

React?并發(fā)

2021-05-06 15:18:09

Shell腳本Linux

2021-04-29 07:46:55

Redis業(yè)務(wù)數(shù)據(jù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)