React中的鉤子函數的功能非常的強大,而它的使用又十分簡單。關於鉤子函數的使用,我們只需記住兩點:
1. 鉤子只能在React組件和自定義鉤子中使用 2. 鉤子不能在嵌套函數或其他語句(if、switch、white、for等)中使用
React中自帶的鉤子函數
useState useEffect useContext useReducer useCallback useRef useMemo useImperativeHandle useLayoutEffect useDebugValue(18.0新增) useDeferredValue(18.0新增) useTransition(18.0新增) useId(18.0新增) useSyncExternalStore(18.0新增) useInsertionEffect(18.0新增)
UseMemo
useMemo和useCallback十分相似,useCallback用來緩存函數對象,useMemo用來緩存函數的執行結果。在組件中,會有一些函數具有十分的複雜的邏輯,執行速度比較慢。閉了避免這些執行速度慢的函數返回執行,可以通過useMemo來緩存它們的執行結果,像是這樣:
const result = useMemo(()=>{ return 複雜邏輯函數(); },[依賴項])
useMemo中的函數會在依賴項發生變化時執行,注意!是執行,這點和useCallback不同,useCallback是創建。執行後返回執行結果,如果依賴項不發生變化,則一直會返回上次的結果,不會再執行函數。這樣一來就避免複雜邏輯的重複執行。
UseImperativeHandle
在React中可以通過forwardRef來指定要暴露給外部組件的ref:
const TestButton = forwardRef((props, ref) => { return <button ref={ref}>Test</button> });
上例中,TestButton組件將button的ref作爲組件的ref向外部暴露,其他組件在使用TestButton時,就可以通過ref屬性訪問:
<TestButton ref={btnRef}/>
通過useImperativeHandle可以手動的指定ref要暴露的對象,比如可以修改TestButton組件如下:
const TestButton = forwardRef((props, ref) => { useImperativeHandle(ref,()=> { return { name:'test-button' }; }); return <button>test</button> });
useImperativeHandle的第二個參數是一個函數,函數的返回值會自動賦值給ref(current屬性)。上例中,我們將返回值爲{name:'test-button'}
。實際開發中,我們可以將一些操作方法定義到對象中,這樣可以有效的減少組件對DOM對象的直接操作。
const TestButton = forwardRef((props, ref) => { const btnRef = useRef(); useImperativeHandle(ref,()=> { return { setDisabled(){ btnRef.current.disabled = true; } }; }); return <button ref={btnRef}>Disable</button> }); const App = () => { const btnRef = useRef(); const clickHandler = () => { btnRef.current.setDisabled(); }; return <div> <TestButton ref={btnRef}/> <button onClick={clickHandler}>ok</button> </div>; };
UseLayoutEffect
useLayoutEffect的方法簽名和useEffect一樣,功能也類似。不同點在於,useLayoutEffect的執行時機要早於useEffect,它會在DOM改變後調用。在老版本的React中它和useEffect的區別比較好演示,React18中,useEffect的運行方式有所變化,所以二者區別不好演示。useLayoutEffect使用場景不多,實際開發中,在effect中需要修改元素樣式,且使用useEffect會出現閃爍現象時可以使用useLayoutEffect進行替換。
UseDebugValue
用來給自定義鉤子設置標籤,標籤會在React開發工具中顯示,用來調試自定義鉤子,不常用。
UseDeferredValue
useDeferredValue用來設置一個延遲的state,比如我們創建一個state,並使用useDeferredValue獲取延遲值:
const [queryMember, setQueryMember] = useState('');
const deferredQueryMember = useDeferredValue(queryMember);
上邊的代碼中queryMember就是一個常規的state,deferredQueryMember就是queryMember的延遲值。設置延遲值後每次調用setState後都會觸發兩次組件的重新渲染。第一次時,deferredQueryMember的值是queryMember修改前的值,第二次纔是修改後的值。換句話,延遲值相較於state來說總會慢一步更新。延遲值可以用在這樣一個場景,一個state需要在多個組件中使用。一個組件的渲染比較快,而另一個組件的渲染比較慢。這樣我們可以爲該state創建一個延遲值,渲染快的組件使用正常的state優先顯示。渲染慢的組件使用延遲值,慢一步渲染。當然必須結合React.memo或useMemo才能真正的發揮出它的作用。
UseTransition
當我們在組件中修改state時,會遇到複雜一些的state,當修改這些state時,甚至會阻塞到整個應用的運行,爲了降低這種state的影響,React爲我們提供了useTransition,通過useTransition可以降低setState的優先級。useTransition會返回一個數組,數組中有兩個元素,第一個元素是isPending,它是一個變量用來記錄transition是否在執行中。第二個元素是startTransition,它是一個函數,可以將setState在其回調函數中調用,這樣setState方法會被標記爲transition並不會立即執行,而是在其他優先級更高的方法執行完畢,纔會執行。除了useTransition外,React還直接爲爲我們提供了一個startTransition函數,在不需要使用isPending時,可以直接使用startTransition也可以達到相同的效果。
UseId
生成唯一id,使用於需要唯一id的場景,但不適用於列表的key。