React 18中,引入了一個新概念——transition
,由此帶來了一個新的API——startTransition
和兩個新的hooks——useTransition
和usedeferredValue
,本文由此展開使用嚐鮮介紹。【相關推薦:Redis視訊教學】
1. 總覽
本文分為4部分進行:
tansition
產生初衷startTransition
使用和介紹useTransition
使用和介紹useDeferredValue
使用和介紹2. transition產生初衷
transtion
直接翻譯為 過渡
。tansition本質上是為了解決渲染並行問題所提出
。在React中一旦元件狀態改變並觸發了重新渲染,則無法停止渲染。直到元件重新渲染完畢,頁面才能繼續響應使用者的互動。
為此react 18中更新都可以劃分為以下兩類:
緊急更新
(urgent update):使用者期望馬上響應的更新操作,例如滑鼠單擊或鍵盤輸入。過渡更新
(transition update):一些延遲可以接受的更新操作,如查詢時,搜尋推薦、搜尋結果的展示等。// 被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 排程機制執行的。
更多程式設計相關知識,請存取:!!
以上就是淺析react18中的新概念Transition的詳細內容,更多請關注TW511.COM其它相關文章!