React Hooks的使用總結(jié)

Hook使用規(guī)則

  1. 確保在React函數(shù)頂層使用Hooks,不能在循環(huán)、條件、嵌套函數(shù)中調(diào)用:當(dāng)單個組件中有多個Hook時,React通過Hooks的調(diào)用順序來保證Hooks的狀態(tài)正確
  2. 只在函數(shù)組件或自定義Hook中調(diào)用

常用Hooks

useState

  1. 在函數(shù)組件內(nèi)部使用useState,可以使該組件變成有狀態(tài)組件。這樣在不使用class組件情況下也可以使用state及其他特性
  2. 使用方法
const [state, setState] = useState(initialState);

參數(shù):狀態(tài)初始值,可以是變量或函數(shù)

  • 變量:初始渲染期間,state與initialState的值相同
  • 函數(shù):如果初始參數(shù)需要通過復(fù)雜的計算得到,可以傳入一個函數(shù),返回initialState。當(dāng)初始狀態(tài)需要通過高性能操作后才能獲得時,使用函數(shù)獲取初始值可以提高性能:該函數(shù)只在初始渲染時被調(diào)用,在后續(xù)組件渲染中不會再調(diào)用

返回值:包含兩個元素的數(shù)組,可解構(gòu)

  • state:狀態(tài)值
  • setState:狀態(tài)更新函數(shù)。調(diào)用后,重新渲染組件,狀態(tài)更新到最新
  1. 惰性初始stateinitialState只會在組件的初始渲染中起作用,后續(xù)渲染時會被忽略
  2. 函數(shù)式更新:新的state依賴于先前的state時,可以為setState方法傳入一個函數(shù),函數(shù)接收先前的state,返回更新后的值
  3. 跳過state更新:若為setState傳入當(dāng)前state,React將跳過子組件的渲染及effect的執(zhí)行
  4. 在多個useState調(diào)用中,渲染之間的調(diào)用順序必須相同
  5. 函數(shù)式更新可以防止合并更新問題
function Bulb(props) {
  const [bulbState, setBulbState] = useState(false);
  const switchBulb = () => {
    setTimeout(() => {
      setBulbState(!bulbState);
      // setBulbState(bulbState => !bulbState);
    }, 3000);
  };
  return (
    <>
      <div className={bulbState ? "bulbOn" : "bulbOff"}>我是燈泡</div>
      <button onClick={switchBulb}>開關(guān)</button>
    </>
  );
}
  • 使用普通更新:由于更新函數(shù)在setTimeout中調(diào)用,在3s內(nèi)多次點擊時,多次state更新會合并成一次,只改變一次state的狀態(tài)
setBulbState(!bulbState);
  • 使用函數(shù)式更新:在3s內(nèi)多次(n)點擊時,每次的更新函數(shù)確保收到的是最新的狀態(tài),所以會在3s后切換n次狀態(tài)
setBulbState(bulbState => !bulbState);

useEffect

  1. useEffect用于執(zhí)行函數(shù)組件中的副作用(Side Effects)。副作用一般包含請求數(shù)據(jù)、事件處理、訂閱、改變DOM等操作。React要求函數(shù)組件必須是一個純函數(shù),其中不能包含副作用。因此,React Hooks提供useEffect,以便在函數(shù)組件中執(zhí)行副作用操作
  2. useEffect默認(rèn)在每次渲染結(jié)束后執(zhí)行
  3. 使用方法
useEffect(() => {
  // Todo
}, [xxx]);

參數(shù):包含命令式、可能有副作用的函數(shù)。第二個參數(shù)可選,為副作用依賴值的數(shù)組

  • 當(dāng)無第二個參數(shù)時:每次組件渲染完成后都會執(zhí)行
  • 當(dāng)?shù)诙€參數(shù)為[]時:空數(shù)組表示effect中沒有依賴props或state中的值,因此僅執(zhí)行一次(僅在組件掛載和卸載時執(zhí)行)。類似于class組件中的componentDidMountcomponentWillUnmount
  • 當(dāng)?shù)诙€參數(shù)為變量數(shù)組時:當(dāng)數(shù)組中的變量發(fā)生變化時,執(zhí)行effect。如果數(shù)組中包含多個變量,當(dāng)該次渲染的數(shù)組與上一次渲染的數(shù)組中的元素都相等時,會跳過本次effect的執(zhí)行,但即使只有一個元素發(fā)生變化,也會執(zhí)行。類似于componentDidUpdate中對prevPropsprevState的判斷
useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // 僅在 count 更改時更新

注意:

  1. 每次渲染都執(zhí)行effect可能會導(dǎo)致性能問題,利用useEffect的第二個參數(shù)可以跳過 Effect 的執(zhí)行,從而進行性能優(yōu)化
  2. 數(shù)組中包含的是所有外部作用域中會隨時間變化并且在effect中使用的變量
  1. 兩種副作用
  • 不需要清除的effect:在渲染完成后執(zhí)行的一些額外的代碼,執(zhí)行后即可忽略。如發(fā)送網(wǎng)絡(luò)請求,手動變更 DOM,記錄日志等
  • 需要清除的effect:如訂閱外部數(shù)據(jù)源、定時器操作等,effect執(zhí)行后要進行清除操作,以防止內(nèi)存泄露
  1. 清除effect:不需要單獨的effect來執(zhí)行清除操作,只需在effect中返回一個清除函數(shù),React將會在執(zhí)行清除操作時調(diào)用該函數(shù)
    清除時機:React會在組件卸載時清理effect,由于effect默認(rèn)每次渲染時都會執(zhí)行,所以React會在調(diào)用一個新的 effect 之前對前一個 effect 進行清理
useEffect(() => {
  // Todo
  return () => {
    // 清除effect操作
  }
});
  1. useEffect會在瀏覽器繪制后、新的渲染前執(zhí)行,因此不會阻止瀏覽器的頁面渲染

useContext

  1. Context為組件樹提供數(shù)據(jù)共享的方法,數(shù)據(jù)無需層層傳遞,即不需要該數(shù)據(jù)的中間層組件無需作為“快遞員”傳遞數(shù)據(jù),避免props層層傳遞過于繁瑣、組件關(guān)系維護困難。具體見文檔
// 創(chuàng)建一個Context上下文
// defaultValue:初始共享值
const MyContext = React.createContext(defaultValue);

// Provider:提供者,提供共享數(shù)據(jù)
// value即共享數(shù)據(jù)
// 當(dāng)value值發(fā)生變化時,Provider中所有使用該共享值的組件都會重新渲染
<MyContext.Provider value={xxx}>  

</MyContext.Provider>

// Consumer:消費者,使用共享數(shù)據(jù)
<MyContext.Consumer>
  {value => /* 基于 context 值進行渲染*/}
</MyContext.Consumer>
  1. useContext即為函數(shù)組件提供的獲取共享數(shù)據(jù)的Hook
  2. 使用方法
const value = useContext(MyContext);

參數(shù):通過React.createContext創(chuàng)建的上下文組件對象
返回值:該上下文的當(dāng)前值,即距離當(dāng)前組件最近<MyContext.Provider>的value

示例:

const animal = {
  dog: {
    name: 'Kiki',
    age: 1
  },
  cat: {
    name: 'Sara',
    age: 2
  }
}
// 創(chuàng)建上下文,傳入初始值
const PetContext = React.createContext(animal);

function App() {
  return (
    <PetContext.Provider value={animal.dog}>
      <MyPet />
    </PetContext.Provider>
  );
}

// 使用共享數(shù)據(jù)的子組件
function MyPet() {
  const pet = useContext(PetContext);
  return (
    <div>Hello, {pet.name}!</div>  // Hello, Kiki!
  );
}
  1. 使用Context的Provider組件包裹函數(shù)組件后,該函數(shù)組件才能共享上下文狀態(tài)
<PetContext.Provider value={animal.dog}>
  <MyPet />
</PetContext.Provider>
  1. 當(dāng)組件依賴的(上層距離最近的)Provider的value值發(fā)生變化時,useContext會觸發(fā)重新渲染,獲取最新的value值
  2. 使用useContext相當(dāng)于在函數(shù)組件中訂閱Context上下文的變化
// 在class組件中的訂閱方式:
static contextType = MyContext
// 或
<MyContext.Consumer>

useReducer

  1. useReduceruseState的替代方案,與Redux中的reducer功能類似,都是提供狀態(tài)改變功能;相對于useState來說,useReducer更適用于state邏輯復(fù)雜且包含多個子值(對象數(shù)組嵌套結(jié)構(gòu))的情景
  2. 使用方法:
const [state, dispatch] = useReducer(reducer, initialArg, init);

參數(shù)

  • reducer:一個函數(shù),接收當(dāng)前狀態(tài)state和觸發(fā)行為action,返回計算后的新的狀態(tài)newState
(state, action) => newState

action:觸發(fā)狀態(tài)改變的行為,是一個對象,包含:
type:表示行為的描述,如增、刪、改、查用戶等;
payload(可選):表示攜帶的數(shù)據(jù),用于newState的計算

const action = {
  type: 'addUser',
  payload: {
    name: 'Bobo',  
    sex: 0    
  }
}

注意:

  • 不要直接修改state!因為它是immutable(React 使用 Object.is 來比較 state
  • 可以通過解構(gòu)賦值創(chuàng)建newState并返回

舉個栗子:

function myReducer(state, action) {
  const {type, payload} = action
  if (type === 'addUser') {
     return [
      ...state,
      payload
     ]
  }
  if (type === 'removeUser') {
     // 移除該用戶,并把新的用戶數(shù)組返回
     // ...
  }
  // type的其他判斷...
  return state
}
  • initialArg:初始狀態(tài)值
  • init:一個方法,用于惰性初始化state,當(dāng)傳該方法時,初始值就變成了 init(initialArg),可以用來重置初始狀態(tài)

返回值:包含兩個元素的數(shù)組,可解構(gòu)

  • state:當(dāng)前狀態(tài)值
  • dispatch:方法,用于觸發(fā)事件以更新state,參數(shù)為一個action對象,即將action中的payload作為參數(shù),執(zhí)行type操作,更改狀態(tài)
// ...
const action = {
  type: 'addUser',
  payload: {
    name: 'Bobo',  
    sex: 0    
  }
}

// ...
<button onClick={() => dispatch(action)}>點擊增加用戶</button>
  1. 跳過dispatch:若返回的state與當(dāng)前的state相同,則不會進行子組件的重新渲染和執(zhí)行副作用
  2. useReducer可以結(jié)合useContext執(zhí)行復(fù)雜的狀態(tài)更新操作。當(dāng)組件嵌套比較深時,可以將dispatch作為共享數(shù)據(jù)傳遞給子組件,這樣在子組件中就可以使用dispatch觸發(fā)事件,更改狀態(tài)。參考此處

useCallback

  1. useCallback用于緩存回調(diào)函數(shù),減少不必要的渲染
  2. 在組件第一次渲染的時候執(zhí)行,之后會在其依賴的變量發(fā)生改變時再次執(zhí)行,返回新的函數(shù)
  3. 使用方法
useCallback(fn, deps)

參數(shù)

  • fn:函數(shù)
  • deps:依賴項

返回值:緩存的函數(shù)

useMemo

  1. useCallback類似,useMemo用于針對返回值進行緩存優(yōu)化
  2. 在組件第一次渲染的時候執(zhí)行,之后會在其依賴的變量發(fā)生改變時再次執(zhí)行,返回新的值
  3. 使用方法
useMemo(() => fn, deps)

參數(shù)

  • fn:函數(shù)
  • deps:依賴項

返回值:緩存的值

useRef

  1. 先來了解一下Refs和DOM,在HTML元素或class組件上可以通過 React.createRef()創(chuàng)建ref引用該元素
  2. 在函數(shù)組件中引入了useRef,用于保存任何可變的值,每次都會返回相同的ref引用
  3. 使用方法
const refContainer = useRef(initialValue);

參數(shù)

  • initialValue:初始值

返回值:可變的 ref 對象,其.current屬性被初始化為傳入的參數(shù)initialValue

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容