react-useTransition hook基礎使用

Bill發表於2022-12-14

useTransition基礎用法

給別人的github庫提的pr,他寫的關於其他hook的使用也很清晰易懂

useTransition概念介紹

react提供了useDeferredValue發揮類似防抖節流的作用,而useTransition也是類似的作用,但是該hook是透過降低資料渲染的優先順序來達到優先更新其他資料

useTransition用來解決什麼問題?

  • 首先給定一個場景,開發時經常會遇到需要聯想輸入,也就是輸入的同時要返回聯想搜尋結果的列表。
  • 但是這個列表有時返回值非常的長,有時會導致使用者輸入值的更新緩慢,這裡就產生了一個問題,當頁面有大量UI更新的時候,怎麼處理資料更新不會卡頓。
  • 可以手寫防抖節流,防抖有一個弊端,當我們長時間的持續輸入(時間間隔小於防抖設定的時間),頁面就會長時間都不到響應。而startTransition 可以指定 UI 的渲染優先順序,哪些需要實時更新,哪些需要延遲更新。即使使用者長時間輸入最遲 5s 也會更新一次。
  • 也可以用useDeferredValue,也可以用“可視視窗載入”的方案,
    在這裡介紹怎麼用useTransition解決

useTransition原始碼

回到useTransition的學習中,首先看一下React原始碼中的ReactHooks.js

export function useTransition(): [
  boolean,
  (callback: () => void, options?: StartTransitionOptions) => void,
] {
  const dispatcher = resolveDispatcher();
  return dispatcher.useTransition();
}

再根據引入檔案,到react-reconciler/src/ReactInternalTypes.js找到Dispatch裡最終呼叫的useTransition

  useTransition(): [
    boolean,
    (callback: () => void, options?: StartTransitionOptions) => void,
  ],

上述程式碼看不懂沒關係,本系列教程只是講述“如何使用Hook”,並不是“Hook原始碼分析”。^_^

useTransition基本用法

useTransition()函式可以不傳參,傳參可以傳一個毫秒值用來修改最遲更新時間,startTransition回撥裡的賦值將會被降低優先順序。isPending 指示過渡任務何時活躍以顯示一個等待狀態。

程式碼形式
const [isPending, startTransition] = useTransition();

startTransition(() => {
  setCount(count + 1);
})

傳參寫法

// 延遲兩秒
const [isPending, startTransition] = useTransition(2000);

startTransition(() => {
  setCount(count + 1);
})

useTransition使用示例

舉例:搜尋引擎的關鍵詞聯想。一般來說,對於使用者在輸入框中輸入都希望是實時更新的,如果此時聯想詞比較多同時也要實時更新的話,這就可能會導致使用者的輸入會卡頓。這樣一來使用者的體驗會變差,這並不是我們想要的結果。

我們將這個場景的狀態更新提取出來:一個是使用者輸入的更新;一個是聯想詞的更新,這個兩個更新緊急程度顯然前者大於後者。

這裡更新效果可能還不夠明顯,可以開啟瀏覽器控制檯,點選performance insights項,在Measure page load右邊有個下拉選項,在cpu那欄的右邊下拉選擇4x slowdown可以將瀏覽器執行速度調慢四倍,這樣卡頓會明顯些。

這裡拆分為兩個元件,父元件是useTransition的使用,子元件是列表渲染,程式碼如下:

父元件:

import { useState, useTransition } from 'react'
import ProductList from './components/ProductList'

// 列表資料的生成
export function generateProducts() {
  const products: Array<string> = []
  for (let i = 0; i < 10000; i++) {
    products.push(`Product ${i + 1}`)
  }
  return products
}

// 列表資料
const dummyProducts = generateProducts()

// 使用者輸入時過濾搜尋,達到一個聯想輸入的效果
function filterProducts(filterTerm) {
  if (!filterTerm) {
    return dummyProducts
  }
  return dummyProducts.filter((product) => product.includes(filterTerm))
}

function App() {
  const [isPending, startTransition] = useTransition()
  const [filterTerm, setFilterTerm] = useState('')

  const filteredProducts = filterProducts(filterTerm)

  function updateFilterHandler(event) {
    // 列表資料賦值的執行等級
    startTransition(() => {
      setFilterTerm(event.target.value)
    })
  }

  return (
    <div id="app">
      <input type="text" onChange={updateFilterHandler} />
      {isPending && <p style={{ color: 'white' }}>更新列表。. </p>}
      <ProductList products={filteredProducts} />
    </div>
  )
}

export default App

子元件:

import { useDeferredValue } from "react";

function ProductList({ products }) {
  const deferredProducts = useDeferredValue(products);
  return (
    <ul>
      {deferredProducts.map((product, index) => (
        <li key={index}>{product}</li>
      ))}
    </ul>
  );
}

export default ProductList;

透過這個案例,相信你對useMemo的機制和用法一定有所掌握。


至此,關於useTransition基礎用法已經講完。

相關文章