
如何快速入門VUE3.0:進入學習
React 18中,引入了一個新概念—— transition ,由此帶來了一個新的API—— startTransition和兩個新的hooks—— useTransition和usedeferredValue ,本文由此展開使用嚐鮮介紹。
1. 總覽
本文分為4部分進行:
tansition產生初衷startTransition使用和介紹useTransition使用和介紹useDeferredValue使用和介紹2. transition產生初衷
transtion直接翻譯為过渡。 tansition本質上是为了解决渲染并发问题所提出。在React中一旦元件狀態改變並觸發了重新渲染,則無法停止渲染。直到元件重新渲染完畢,頁面才能繼續回應使用者的互動。
為此react 18更新都可以劃分為以下兩類:
紧急更新):使用者期望馬上回應的更新操作,例如滑鼠點擊或鍵盤輸入。过渡更新):一些延遲可以接受的更新操作,如查詢時,搜尋推薦、搜尋結果的展示等。// 被startTransiton標記後為過渡更新startTransition(()=> {
// 非緊急更新,會被降低優先權,延遲執行setQueryValue(inputValue)
})
// 未被標記則馬上執行setInputValue(inputValue)在react 18中被startTrionstion標記的更新,即為過渡更新(執行的優先權被降低),此時react會根據內部的調度機制延遲執行內部的state更新。
開發中開發者可以透過transition hook決定哪些更新被標記為transition事件。一旦被標記則代表為低優先級執行,即react知道該state可以延遲更新,通过区分更新优先级,讓高優先級的事件保持回應,提高用户交互体验,保持页面响应。
3. startTransiton
startTransiton使用介紹
const handleClick = () => {
// startTransition包裹標記為低優先權更新startTransition(()=> {
setQueryValue(inputValue)
})
// 未被標記則馬上執行setInputValue(inputValue)
}首先我們來介紹下最簡單的startTransition
透過示範對比
這是一個對輸入字元後展示搜尋結果的場景模擬,透過偽造大量搜尋結果,模擬容易卡頓的情況。
我們試著連續輸入123,監聽搜尋框值value變化(urgent update)和搜尋值searchVal變化(transition update)並輸出到控制欄。
import React, { useEffect, useState, startTransition } from 'react';
import './App.css'
const SearchResult = (props) => {
const resultList = props.query
? Array.from({ length: 10000 }, (_, index) => ({
id: index,
keyword: `${props.query} -- 搜尋結果${index}`,
})) : [];
return resultList.map(({ id, keyword }) => (
<li key={id}>{keyword}</li>
))
}
const App = () => {
const [type, setTpye] = useState(1)
const [value, setValue] = useState('');
const [searchVal, setSearchVal] = useState('-');
useEffect(() => {
// 監聽搜尋值改變console.log('對搜尋值更新的回應++++++' + searchVal + '+++++++++++')
}, [searchVal])
useEffect(() => {
console.log('對輸入框值更新的回應-----' + value + '-------------')
if (type === 1) {
setSearchVal(value || '-')
}
if (type === 2) {
startTransition(() => {
setSearchVal(value || '-')
})
}
}, [value, type]);
return (
<div className='App'>
<input value={value} onChange={e => setValue(e.target.value)} />
<div className={`type_button ${type === 1 ? 'type_button_checked' : ''}`} onClick={() => setTpye(1)}>normal</div>
<div className={`type_button ${type === 2 ? 'type_button_checked' : ''}`} onClick={() => setTpye(2)}>transiton</div>
<ul>
<SearchResult query={searchVal}></SearchResult>
</ul>
</div>
);
};普通模式下

如图所示:連續輸入字元123,當第一個字元輸入後,搜尋值馬上回應,清單渲染立刻開始,造成卡頓輸入框停止了對使用者輸入的回應,直到渲染結束,輸入框才繼續回應。
使用startTransition後

如图所示:連續輸入字元123,輸入框不斷回應,搜尋值的回應被延後,保證頁面回饋,直到輸入結束,才開始回應搜尋值,渲染搜尋結果,保持頁面回應。
4. useTransiton
useTransiton使用介紹
import { useTransiton } from 'react'
const [isPending, startTransition] = useTransiton({timeoutMs: 2000})
// 例如, 在pending狀態下,您可以展示一個Spinner
{ isPending ? < Spinner /> : null } startTransition是一個接受回呼的函數,用於告知React需要延遲更新的state。isPending是一個布林值,這是react告知我們是否等待過渡完成的方式。useTransition接受帶有timeoutMs的延遲回應的值,如果給定的timeoutMs內未完成,它將會強制執行startTransition回呼函數內state的更新。useTransiton簡單分析
我們透過偽代碼理解下useTransition 。
function useTransition(){
const [isPending, setPending] = mountState(false);
const start = (callback)=>{
setPending(true);
// Scheduler.unstable_next 透過transiton 模式,低優先權調度執行回呼函數// 可以降低更新的優先權。如果回呼中觸發的更新優先權會比較低,
// 它會讓位元為高優先權的更新,或是當交易繁忙時,調度到下一空閒期再套用。
Scheduler.unstable_next(() => {
const prevTransition = ReactCurrentBatchConfig.transition;
ReactCurrentBatchConfig.transition = 1;
try {
setPending(false);
//實施回呼函數callback();
} finally {
ReactCurrentBatchConfig.transition = prevTransition;
}
})
}
return [isPending, start];
} startTransition執行過程中,會觸發兩次setPending ,一次在transition=1之前,一次在之後。 startTransition被呼叫時setPending(true) ,當startTransition內部的回呼函數執行時transiton過渡任務更新setPending(false) 。 react內部可以根據pending值的變化準確把握等待的過渡時間,並依此判斷是否超過了timeoutMs (如果有傳入)強制執行更新。
5. useDeferredValue
useDeferredValue使用介紹
const [value, setValue] = useState('')
// defferedValue值延後於state更新const deferredValue = useDeferredValue(value, {timeoutMs: 2000}) timeoutMs 。一段逻辑,而useDeferred是產生一个新状态。useDeferredValue的使用
import React, { useEffect, useState, useTransition, useDeferredValue } from 'react';
import './App.css'
const SearchResult = (props) => {
const resultList = props.query
? Array.from({ length: 10000 }, (_, index) => ({
id: index,
keyword: `${props.query} -- 搜尋結果${index}`,
})) : [];
return resultList.map(({ id, keyword }) => (
<li key={id}>{keyword}</li>
))
}
const App = () => {
const [value, setValue] = useState('');
const searchValue = useDeferredValue(value, { timeoutMs: 2000 });
useEffect(() => {
console.log('對輸入框值的反應--------' + value + '---------------')
}, [value])
useEffect(() => {
// 監聽搜尋值改變console.log('對搜尋值的更新回應++++++' + searchValue + '+++++++++++')
}, [searchValue])
return (
<div className='App'>
<input value={value} onChange={e => setValue(e.target.value)} />
<div className={`type_button type_button_checked`}>useDeferredValue</div>
<ul>
<SearchResult query={searchValue}></SearchResult>
</ul>
</div>
);
}; 
useDeferredValue簡單分析
我們透過偽代碼理解下useDeferredValue 。
function useDeferredValue(value){
const [prevValue, setValue] = updateState(value);
updateEffect(() => {
// 在useEffect 中透過transition 模式來更新value 。
Scheduler.unstable_next(() => {
const prevTransition = ReactCurrentBatchConfig.transition;
ReactCurrentBatchConfig.transition = 1;
try {
setValue(value);
} finally {
ReactCurrentBatchConfig.transition = prevTransition;
}
})
}, [value]);
return prevValue;
} useDeferredValue透過useEffect監聽傳入值的變化,然後透過過渡任務執行值的改變。這樣保证defrredValue的更新滞后于setState ,同時符合過渡更新的原則,因為是透過transition 調度機制執行的。