AVt天堂网 手机版,亚洲va久久久噜噜噜久久4399,天天综合亚洲色在线精品,亚洲一级Av无码毛片久久精品

當前位置:首頁 > 科技  > 軟件

ReactHooks由淺入深:所有 hooks 的梳理、匯總與解析

來源: 責編: 時間:2024-04-19 09:29:18 158觀看
導讀Vue 中的指令、React 中的 hooks 都是框架的核心知識點。但是對于很多同學來說,因為日常工作中開發的局限性,所以對這些 指令 或 hooks 認知的并不全面,一旦在面試的時候被問到不熟悉的 指令 或者 hooks 可能就會吃虧。

Vue 中的指令、React 中的 hooks 都是框架的核心知識點。但是對于很多同學來說,因為日常工作中開發的局限性,所以對這些 指令 或 hooks 認知的并不全面,一旦在面試的時候被問到不熟悉的 指令 或者 hooks 可能就會吃虧。CtJ28資訊網——每日最新資訊28at.com

CtJ28資訊網——每日最新資訊28at.com

所以說,咱們今天就先來整理一下 React 中的 hooks,整理的過程會 由淺入深 同時配合一些代碼進行說明。爭取讓哪怕不是很熟悉 react 的同學,也可以在本文章中有一定的收獲。CtJ28資訊網——每日最新資訊28at.com

一、React 基礎普及

1、什么是 react 中的組件

在 React 16.8 之后,react 使用 函數 表示組件,稱為 函數式組件CtJ28資訊網——每日最新資訊28at.com

例如以下代碼,就是兩個基礎函數式組件(App 與 Greeting):CtJ28資訊網——每日最新資訊28at.com

import React from 'react';// 函數組件function Greeting(props) {  return <h1>Hello, {props.name}!</h1>;}// 使用函數組件function App() {  return (    <div>      <Greeting name="Alice" />      <Greeting name="Bob" />      <Greeting name="Charlie" />    </div>  );}export default App;

2、什么是 react hooks

在 React 中 以 use 開頭的函數 就被稱之為 hooks。CtJ28資訊網——每日最新資訊28at.com

React 默認提供一些 hooks。同樣的,我們也可以自定義一些函數(以 use)開頭,那么該函數就可以被稱為是 hooks。CtJ28資訊網——每日最新資訊28at.com

import { useState } from 'react';// 最簡單的自定義Hooksfunction useMyCustomHook() {  // 在這個例子中,我們只是返回一個固定的值  const [value] = useState("Hello, World!");  return value;}export default useMyCustomHook;

3、hooks 解決了什么問題?

如果沒有Hooks,函數組件的功能相對有限,只能接受 Props、渲染UI,以及響應來自父組件的事件。CtJ28資訊網——每日最新資訊28at.com

因此,Hooks的出現主要是為了解決是哪個問題:CtJ28資訊網——每日最新資訊28at.com

  • 強化函數式組件:讓函數組件具備類組件(16.8之前)的功能,可以有自己的狀態、處理副作用、獲取ref,以及進行數據緩存。
  • 提高復用性: hooks本質上是以 use 開頭的函數,通過函數封裝邏輯,解決邏輯復用的問題。
  • 函數式編程:react 推薦函數式編程,摒棄面向對象編程。

二、React Hooks 劃分

React Hooks 根據性能可以劃分為 5 大類:CtJ28資訊網——每日最新資訊28at.com

CtJ28資訊網——每日最新資訊28at.com

1.1 狀態處理:useState

useState是React提供的一個Hook,它讓函數組件也能像類組件一樣擁有狀態。通過useState,你可以讓組件在內部管理一些數據,并在數據更新時重新渲染視圖。CtJ28資訊網——每日最新資訊28at.com

在使用useState時,你會得到一個包含兩個值的數組:CtJ28資訊網——每日最新資訊28at.com

  1. state:當前狀態的值,它用于提供給UI,作為渲染視圖的數據源。
  2. dispatch:一個函數,用于改變state的值,從而觸發函數組件的重新渲染。

useState的基礎用法如下:CtJ28資訊網——每日最新資訊28at.com

const DemoState = (props) => {   // number是當前state的值,setNumber是用于更新state的函數   let [number, setNumber] = useState(0) // 0為初始值   return (       <div>           <span>{number}</span>           <button onClick={() => {             setNumber(number + 1)             console.log(number) // 這里的number是不能夠即時改變的           }}>增加</button>       </div>   )}

在使用useState時需要注意:CtJ28資訊網——每日最新資訊28at.com

  1. 在同一個函數組件的執行過程中,state的值是固定不變的。比如,如果你在一個定時器中改變state的值,組件不會重新渲染。
  2. 如果兩次dispatch傳入相同的state值,那么組件就不會更新,因為React會認為狀態沒有改變。
  3. 當你在當前執行上下文中觸發dispatch時,無法立即獲取到最新的state值,只有在下一次組件重新渲染時才能獲取到。

1.2 狀態處理:useReducer

useReducer是React Hooks提供的一個功能,類似于Redux的狀態管理工具。CtJ28資訊網——每日最新資訊28at.com

在使用useReducer時,你會得到一個包含兩個值的數組:CtJ28資訊網——每日最新資訊28at.com

  1. state:state是更新后的狀態值
  2. dispatch:dispatch是用于派發更新的函數,與useState中的dispatch函數類似

基礎用法如下:CtJ28資訊網——每日最新資訊28at.com

const DemoUseReducer = () => {    // number為更新后的state值, dispatchNumbner為當前的派發函數    const [number, dispatchNumber] = useReducer((state, action) => {        const { payload, name } = action;        // 根據不同的action類型來更新state        switch (name) {            case 'add':                return state + 1;            case 'sub':                return state - 1;            case 'reset':                return payload;            default:                return state;        }    }, 0);    return (        <div>            當前值:{number}            {/* 派發更新 */}            <button onClick={() => dispatchNumber({ name: 'add' })}>增加</button>            <button onClick={() => dispatchNumber({ name: 'sub' })}>減少</button>            <button onClick={() => dispatchNumber({ name: 'reset', payload: 666 })}>賦值</button>            {/* 把dispatch和state傳遞給子組件 */}            <MyChildren dispatch={dispatchNumber} state={{ number }} />        </div>    );};

在useReducer中,你需要傳入一個reducer函數,這個函數接受當前的state和一個action作為參數,并返回新的state。如果新的state和之前的state指向的是同一個內存地址,那么組件就不會更新。CtJ28資訊網——每日最新資訊28at.com

1.3 狀態處理:useSyncExternalStore

useSyncExternalStore的出現與React版本18中更新模式下外部數據的撕裂(tearing)密切相關。它允許React組件在并發模式下安全有效地讀取外部數據源,并在組件渲染過程中檢測數據的變化,以及在數據源發生變化時調度更新,以確保結果的一致性。CtJ28資訊網——每日最新資訊28at.com

基礎介紹如下:CtJ28資訊網——每日最新資訊28at.com

useSyncExternalStore(    subscribe,    getSnapshot,    getServerSnapshot)
  1. subscribe是一個訂閱函數,當數據發生變化時會觸發該函數。useSyncExternalStore會使用帶有記憶功能的getSnapshot來判斷數據是否發生變化,如果有變化,則強制更新數據。
  2. getSnapshot可以看作是帶有記憶功能的選擇器。當數據源變化時,通過getSnapshot生成新的狀態值,該狀態值可作為組件的數據源使用。getSnapshot能夠檢查訂閱的值是否發生變化,一旦發生變化,則觸發更新。
  3. getServerSnapshot用于hydration模式下的getSnapshot。

基礎用法示例如下:CtJ28資訊網——每日最新資訊28at.com

import { combineReducers, createStore } from 'redux';/* number Reducer */function numberReducer(state = 1, action) {    switch (action.type) {        case 'ADD':            return state + 1;        case 'DEL':            return state - 1;        default:            return state;    }}/* 注冊reducer */const rootReducer = combineReducers({ number: numberReducer });/* 創建 store */const store = createStore(rootReducer, { number: 1 });function Index() {    /* 訂閱外部數據源 */    const state = useSyncExternalStore(store.subscribe, () => store.getState().number);    console.log(state);    return (        <div>            {state}            <button onClick={() => store.dispatch({ type: 'ADD' })}>點擊</button>        </div>    );}

當點擊按鈕時,會觸發reducer,然后會觸發store.subscribe訂閱函數,執行getSnapshot得到新的number,判斷number是否發生變化,如果有變化,則觸發更新。CtJ28資訊網——每日最新資訊28at.com

1.4 狀態處理:useTransition

在React v18中,引入了一種新概念叫做過渡任務。這些任務與立即更新任務相對應,通常指的是一些不需要立即響應的更新,比如頁面從一個狀態過渡到另一個狀態。CtJ28資訊網——每日最新資訊28at.com

舉個例子,當用戶點擊tab從tab1切換到tab2時,會產生兩個更新任務:CtJ28資訊網——每日最新資訊28at.com

  1. 第一個任務是hover狀態從tab1變成tab2。
  2. 第二個任務是內容區域從tab1內容變換到tab2內容。

這兩個任務中,用戶通常希望hover狀態的響應更迅速,而內容的響應可能需要更長時間,比如請求數據等操作。因此,第一個任務可以視為立即執行任務,而第二個任務則可以視為過渡任務。CtJ28資訊網——每日最新資訊28at.com

useTransition的基礎介紹如下:

import { useTransition } from 'react';/* 使用 */const [isPending, startTransition] = useTransition();

useTransition會返回一個數組,其中包含兩個值:CtJ28資訊網——每日最新資訊28at.com

  1. 第一個值是一個標志,表示是否處于過渡狀態。
  2. 第二個值是一個函數,類似于上述的startTransition,用于將更新任務轉換為過渡任務。

在基礎用法中,除了切換tab的場景外,還有很多其他場景適合產生過渡任務,比如實時搜索并展示數據。這種情況下,有兩個優先級的任務:第一個是受控表單的實時響應,第二個是輸入內容改變后數據展示的變化。CtJ28資訊網——每日最新資訊28at.com

下面是一個基本使用useTransition的示例:CtJ28資訊網——每日最新資訊28at.com

const mockList1 = new Array(10000).fill('tab1').map((item,index)=>item+'--'+index )const mockList2 = new Array(10000).fill('tab2').map((item,index)=>item+'--'+index )const mockList3 = new Array(10000).fill('tab3').map((item,index)=>item+'--'+index )const tab = {  tab1: mockList1,  tab2: mockList2,  tab3: mockList3}export default function Index(){  const [ active, setActive ] = React.useState('tab1') //立即響應的任務,立即更新任務  const [ renderData, setRenderData ] = React.useState(tab[active]) //不需要立即響應的任務,過渡任務  const [ isPending,startTransition  ] = React.useTransition()   const handleChangeTab = (activeItem) => {     setActive(activeItem) //立即更新     startTransition(()=>{ //startTransition里面的任務優先級低       setRenderData(tab[activeItem])     })  }  return <div>    <div className='tab' >       { Object.keys(tab).map((item)=> <span className={ active === item && 'active' } onClick={()=>handleChangeTab(item)} >{ item }</span> ) }    </div>    <ul className='content' >       { isPending && <div> loading... </div> }       { renderData.map(item=> <li key={item} >{item}</li>) }    </ul>  </div>}

以上示例中,當切換tab時,會產生兩個優先級任務:第一個任務是setActive控制tab的active狀態的改變,第二個任務是setRenderData控制渲染的長列表數據(在實際場景中,這可能是一些數據量大的可視化圖表)。CtJ28資訊網——每日最新資訊28at.com

1.5 狀態處理:useDeferredValue

在React 18中,引入了useDeferredValue,它可以讓狀態的更新滯后于派生。useDeferredValue的實現效果類似于transition,在緊急任務執行后,再得到新的狀態,這個新的狀態就稱之為DeferredValue。CtJ28資訊網——每日最新資訊28at.com

useDeferredValue基礎介紹:

useDeferredValue和前面提到的useTransition有什么異同呢?CtJ28資訊網——每日最新資訊28at.com

相同點: useDeferredValue和useTransition本質上都是標記為過渡更新任務。CtJ28資訊網——每日最新資訊28at.com

不同點: useTransition將內部的更新任務轉換為過渡任務transition,而useDeferredValue則是通過過渡任務得到新的值,這個值作為延遲狀態。一個是處理一段邏輯,另一個是生成一個新的狀態。CtJ28資訊網——每日最新資訊28at.com

useDeferredValue接受一個參數value,通常是可變的state,返回一個延遲狀態deferredValue。CtJ28資訊網——每日最新資訊28at.com

const deferredValue = React.useDeferredValue(value)

useDeferredValue基礎用法:

下面將上面的例子改用useDeferredValue來實現。CtJ28資訊網——每日最新資訊28at.com

export default function Index(){  const [ active, setActive ] = React.useState('tab1') //需要立即響應的任務,立即更新任務  const deferredActive = React.useDeferredValue(active) // 將狀態延遲更新,類似于過渡任務  const handleChangeTab = (activeItem) => {     setActive(activeItem) // 立即更新  }  const renderData = tab[deferredActive] // 使用延遲狀態  return <div>    <div className='tab' >       { Object.keys(tab).map((item)=> <span className={ active === item && 'active' } onClick={()=>handleChangeTab(item)} >{ item }</span> ) }    </div>    <ul className='content' >       { renderData.map(item=> <li key={item} >{item}</li>) }    </ul>  </div>}

上述代碼中,active是正常改變的狀態,deferredActive是延遲的active狀態。我們使用正常狀態來改變tab的active狀態,而使用延遲狀態來更新視圖,從而提升了用戶體驗。CtJ28資訊網——每日最新資訊28at.com

2.1 副作用執行:useEffect

React hooks提供了API,用于彌補函數組件沒有生命周期的不足。主要利用了hooks中的useEffect、useLayoutEffect和useInsertionEffect。其中,最常用的是useEffect。現在我們來看一下useEffect的使用。CtJ28資訊網——每日最新資訊28at.com

useEffect基礎介紹:

useEffect(() => {    return cleanup;}, dependencies);

useEffect的第一個參數是一個回調函數,返回一個清理函數cleanup。cleanup函數會在下一次回調函數執行之前調用,用于清除上一次回調函數產生的副作用。CtJ28資訊網——每日最新資訊28at.com

第二個參數是一個依賴項數組,里面可以包含多個依賴項。當依賴項發生變化時,會執行上一次callback返回的cleanup函數,并執行新的effect回調函數。CtJ28資訊網——每日最新資訊28at.com

對于useEffect的執行,React采用了異步調用的處理邏輯。對于每個effect的回調函數,React會將其放入任務隊列中,類似于setTimeout回調函數的方式,等待主線程任務完成、DOM更新、JS執行完成以及視圖繪制完成后才執行。因此,effect回調函數不會阻塞瀏覽器的視圖繪制。CtJ28資訊網——每日最新資訊28at.com

useEffect基礎用法:

/* 模擬數據交互 */function getUserInfo(a){    return new Promise((resolve)=>{        setTimeout(()=>{            resolve({               name:a,               age:16,           })         },500)    })}const Demo = ({ a }) => {    const [userMessage, setUserMessage] = useState({});    const div = useRef();    const [number, setNumber] = useState(0);    /* 模擬事件監聽處理函數 */    const handleResize = () => {};    /* useEffect使用 */    useEffect(() => {       /* 請求數據 */       getUserInfo(a).then(res => {           setUserMessage(res);       });       /* 定時器 延時器等 */       const timer = setInterval(() => console.log(666), 1000);       /* 操作dom */       console.log(div.current); /* div */       /* 事件監聽等 */       window.addEventListener('resize', handleResize);       /* 此函數用于清除副作用 */       return function() {           clearInterval(timer);           window.removeEventListener('resize', handleResize);       };    /* 只有當props->a和state->number改變的時候, useEffect副作用函數重新執行,如果此時數組為空[],證明函數只有在初始化的時候執行一次,相當于componentDidMount */    }, [a, number]);    return (        <div ref={div}>            <span>{userMessage.name}</span>            <span>{userMessage.age}</span>            <div onClick={() => setNumber(1)}>{number}</div>        </div>    );};

上述代碼中,在useEffect中做了以下功能:CtJ28資訊網——每日最新資訊28at.com

  1. 請求數據。
  2. 設置定時器、延時器等。
  3. 操作DOM,在React Native中可以通過ref獲取元素位置信息等內容。
  4. 注冊事件監聽器,在React Native中可以注冊NativeEventEmitter。
  5. 清除定時器、延時器、解綁事件監聽器等。

2.2 副作用執行:useLayoutEffect

useLayoutEffect基礎介紹:

useLayoutEffect和useEffect的不同之處在于它采用了同步執行的方式。那么它和useEffect有什么區別呢?CtJ28資訊網——每日最新資訊28at.com

① 首先,useLayoutEffect在DOM更新之后、瀏覽器繪制之前執行。這使得我們可以方便地修改DOM、獲取DOM信息,從而避免了不必要的瀏覽器回流和重繪。相比之下,如果將DOM布局修改放在useEffect中,那么useEffect的執行是在瀏覽器繪制視圖之后進行的,接著再去修改DOM,可能會導致瀏覽器進行額外的回流和重繪。由于兩次繪制,可能會導致視圖上出現閃現或突兀的效果。CtJ28資訊網——每日最新資訊28at.com

② useLayoutEffect回調函數中的代碼執行會阻塞瀏覽器的繪制。CtJ28資訊網——每日最新資訊28at.com

useLayoutEffect基礎用法:

const DemoUseLayoutEffect = () => {    const target = useRef();    useLayoutEffect(() => {        /* 在DOM繪制之前,移動DOM到指定位置 */        const { x, y } = getPositon(); // 獲取要移動的x,y坐標        animate(target.current, { x, y });    }, []);    return (        <div>            <span ref={target} className="animate"></span>        </div>    );};

2.3 副作用執行:useInsertionEffect

useInsertionEffect基礎介紹:

useInsertionEffect是React v18新增的hooks之一,其用法與useEffect和useLayoutEffect相似。那么這個hooks用于什么呢?CtJ28資訊網——每日最新資訊28at.com

在介紹useInsertionEffect用途之前,先來看一下useInsertionEffect的執行時機。CtJ28資訊網——每日最新資訊28at.com

React.useEffect(() => {    console.log('useEffect 執行');}, []);React.useLayoutEffect(() => {    console.log('useLayoutEffect 執行');}, []);React.useInsertionEffect(() => {    console.log('useInsertionEffect 執行');}, []);

打印結果為:useInsertionEffect執行 -> useLayoutEffect執行 -> useEffect執行。CtJ28資訊網——每日最新資訊28at.com

可以看到,useInsertionEffect的執行時機要比useLayoutEffect提前。在useLayoutEffect執行時,DOM已經更新了,但是在useInsertionEffect執行時,DOM還沒有更新。useInsertionEffect主要是解決CSS-in-JS在渲染中注入樣式的性能問題。這個hooks主要適用于這個場景,在其他場景下React不建議使用這個hooks。CtJ28資訊網——每日最新資訊28at.com

useInsertionEffect模擬使用:

export default function Index() {    React.useInsertionEffect(() => {        /* 動態創建style標簽插入到head中 */        const style = document.createElement('style');        style.innerHTML = `            .css-in-js {                color: red;                font-size: 20px;            }        `;        document.head.appendChild(style);    }, []);    return <div className="css-in-js">hello, useInsertionEffect</div>;}

上述代碼模擬了useInsertionEffect的使用。CtJ28資訊網——每日最新資訊28at.com

3.1 狀態傳遞:useContext

useContext基礎介紹

可以使用useContext來獲取父級組件傳遞過來的context值,這個值是最近的父級組件Provider設置的value值。useContext的參數通常是由createContext方式創建的context對象,也可以是父級上下文context傳遞的(參數為context)。useContext可以代替context.Consumer來獲取Provider中保存的value值。CtJ28資訊網——每日最新資訊28at.com

const contextValue = useContext(context);

useContext接受一個參數,一般是context對象,返回值是context對象內部保存的value值。CtJ28資訊網——每日最新資訊28at.com

useContext基礎用法:

/* 用useContext方式 */const DemoContext = () => {    const value = useContext(Context);    /* my name is alien */    return <div> my name is {value.name}</div>;}/* 用Context.Consumer方式 */const DemoContext1 = () => {    return (        <Context.Consumer>            {/* my name is alien */}            {value => <div> my name is {value.name}</div>}        </Context.Consumer>    );}export default () => {    return (        <div>            <Context.Provider value={{ name: 'alien', age: 18 }}>                <DemoContext />                <DemoContext1 />            </Context.Provider>        </div>    );}

3.2 狀態傳遞:useRef

useRef基礎介紹:

useRef可以用來獲取元素,緩存狀態。它接受一個初始狀態initState作為初始值,并返回一個ref對象cur。cur對象上有一個current屬性,該屬性就是ref對象需要獲取的內容。CtJ28資訊網——每日最新資訊28at.com

const cur = React.useRef(initState);console.log(cur.current);

useRef基礎用法:

獲取DOM元素: 在React中,可以利用useRef來獲取DOM元素。在React Native中雖然沒有DOM元素,但是同樣可以利用useRef來獲取組件的節點信息(Fiber信息)。CtJ28資訊網——每日最新資訊28at.com

const DemoUseRef = () => {    const dom = useRef(null);    const handleSubmit = () => {        console.log(dom.current); // <div>表單組件</div> DOM節點    }    return (        <div>            {/* ref標記當前DOM節點 */}            <div ref={dom}>表單組件</div>            <button onClick={handleSubmit}>提交</button>        </div>    );}

保存狀態: 可以利用useRef返回的ref對象來保存狀態,只要當前組件不被銷毀,狀態就會一直存在。CtJ28資訊網——每日最新資訊28at.com

const status = useRef(false);/* 改變狀態 */const handleChangeStatus = () => {    status.current = true;}

3.3 狀態傳遞:useImperativeHandle

useImperativeHandle基礎介紹:

useImperativeHandle配合forwardRef可以自定義向父組件暴露的實例值。對于函數組件,如果我們想讓父組件能夠獲取子組件的實例,就可以使用useImperativeHandle和forwardRef來實現。CtJ28資訊網——每日最新資訊28at.com

useImperativeHandle接受三個參數:CtJ28資訊網——每日最新資訊28at.com

  1. 第一個參數ref:接受forwardRef傳遞過來的ref。
  2. 第二個參數createHandle:處理函數,返回值作為暴露給父組件的ref對象。
  3. 第三個參數deps:依賴項deps,當依賴項改變時形成新的ref對象。

useImperativeHandle基礎用法:

我們通過一個示例來說明,使用useImperativeHandle使得父組件能夠控制子組件中的input自動聚焦并設置值。CtJ28資訊網——每日最新資訊28at.com

function Son(props, ref) {    const inputRef = useRef(null);    const [inputValue, setInputValue] = useState('');    useImperativeHandle(ref, () => {        const handleRefs = {            onFocus() {                inputRef.current.focus();            },            onChangeValue(value) {                setInputValue(value);            }        };        return handleRefs;    }, []);    return (        <div>            <input                placeholder="請輸入內容"                ref={inputRef}                value={inputValue}            />        </div>    );}const ForwardSon = forwardRef(Son);class Index extends React.Component {    inputRef = null;    handleClick() {        const { onFocus, onChangeValue } = this.inputRef;        onFocus();        onChangeValue('let us learn React!');    }    render() {        return (            <div style={{ marginTop: '50px' }}>                <ForwardSon ref={(node) => (this.inputRef = node)} />                <button onClick={this.handleClick.bind(this)}>操控子組件</button>            </div>        );    }}

4.1 性能優化:useMemo

useMemo基礎介紹:

useMemo 可以在函數組件的渲染過程中同步執行一個函數邏輯,并將其返回值作為一個新的狀態進行緩存。這個 hooks 的作用在于優化性能,避免不必要的重復計算或渲染。CtJ28資訊網——每日最新資訊28at.com

基本語法:

const cachedValue = useMemo(create, deps)
  • create: 第一個參數是一個函數,函數的返回值將作為緩存值。
  • deps: 第二個參數是一個數組,包含當前 useMemo 的依賴項。當這些依賴項發生變化時,會重新執行 create 函數以獲取新的緩存值。
  • cachedValue: 返回值,為 create 函數的返回值或上一次的緩存值。

基本用法:CtJ28資訊網——每日最新資訊28at.com

1. 派生新狀態:

function Scope() {    const keeper = useKeep()    const { cacheDispatch, cacheList, hasAliveStatus } = keeper       const contextValue = useMemo(() => {        return {            cacheDispatch: cacheDispatch.bind(keeper),            hasAliveStatus: hasAliveStatus.bind(keeper),            cacheDestory: (payload) => cacheDispatch.call(keeper, { type: ACTION_DESTORY, payload })        }          }, [keeper])    return (        <KeepaliveContext.Provider value={contextValue}>        </KeepaliveContext.Provider>    )}

在上面的示例中,通過 useMemo 派生出一個新的狀態 contextValue,只有 keeper 發生變化時,才會重新生成 contextValue。CtJ28資訊網——每日最新資訊28at.com

2. 緩存計算結果:

function Scope(){    const style = useMemo(()=>{      let computedStyle = {}      // 大量的計算      return computedStyle    },[])    return <div style={style} ></div>}

在這個例子中,通過 useMemo 緩存了一個計算結果 style,只有當依賴項發生變化時,才會重新計算 style。CtJ28資訊網——每日最新資訊28at.com

3. 緩存組件,減少子組件重渲染次數:

function Scope ({ children }){   const renderChild = useMemo(()=>{ children()  },[ children ])   return <div>{ renderChild } </div>}

通過 useMemo 緩存了子組件的渲染結果 renderChild,只有當 children 發生變化時,才會重新執行子組件的渲染。CtJ28資訊網——每日最新資訊28at.com

4.2 性能優化:useCallback

useCallback基礎介紹:

useCallback 和 useMemo 接收的參數類似,都是在其依賴項發生變化后才執行,都返回緩存的值。但是它們的區別在于,useMemo 返回的是函數運行的結果,而 useCallback 返回的是一個經過處理的函數本身。它主要用于優化性能,避免不必要的函數重新創建,特別是在向子組件傳遞函數時,避免因為函數重新創建而導致子組件不必要的重新渲染。CtJ28資訊網——每日最新資訊28at.com

基本用法:

const cachedCallback = useCallback(callbackFunction, deps)
  • callbackFunction: 第一個參數是一個函數,需要進行緩存的函數。
  • deps: 第二個參數是一個數組,包含當前 useCallback 的依賴項。當這些依賴項發生變化時,會重新創建新的緩存函數。
  • cachedCallback: 返回值,為經過處理的緩存函數。

基礎用法示例:

const DemoChildren = React.memo((props)=>{    console.log('子組件更新')    useEffect(()=>{        props.getInfo('子組件')    },[])    return <div>子組件</div>})const DemoUseCallback=({ id })=>{    const [number, setNumber] = useState(1)        const getInfo  = useCallback((sonName)=>{        console.log(sonName)    }, [id]) // 只有當 id 發生變化時,才會重新創建 getInfo 函數        return (        <div>            <button onClick={ ()=>setNumber(number+1) }>增加</button>            <DemoChildren getInfo={getInfo} />        </div>    )}

在上面的示例中,getInfo 函數通過 useCallback 進行了緩存,只有當 id 發生變化時,才會重新創建 getInfo 函數。這樣可以避免因為函數重新創建而導致子組件不必要的重新渲染。CtJ28資訊網——每日最新資訊28at.com

5.1 工具類:useId

在組件的頂層調用 useId 生成唯一 ID:CtJ28資訊網——每日最新資訊28at.com

import { useId } from 'react';function PasswordField() {  const passwordHintId = useId();  // ...

5.2 工具類:useDebugValue

在你的 自定義 Hook 的頂層調用 useDebugValue,以顯示可讀的調試值:CtJ28資訊網——每日最新資訊28at.com

import { useDebugValue } from 'react';function useOnlineStatus() {  // ...  useDebugValue(isOnline ? 'Online' : 'Offline');  // ...}


CtJ28資訊網——每日最新資訊28at.com

本文鏈接:http://www.tebozhan.com/showinfo-26-84029-0.htmlReactHooks由淺入深:所有 hooks 的梳理、匯總與解析

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: React狀態管理專題:利用Context API解決屬性鉆取問題

下一篇: 虛擬現實:游戲、人工智能和沉浸式體驗的未來

標簽:
  • 熱門焦點
Top