React基礎(chǔ)之Hook

本文旨在介紹常用的Hook的用途和用法。官方文檔地址

1. State Hook

  • useState

    • 功能: 在組件的頂層調(diào)用 useState 來聲明一個(gè) 狀態(tài)變量。

    • 使用示例

      import { useState } from 'react';
      // 聲明狀態(tài)變量
      const [age, setAge] = useState(28); // 唯一的參數(shù)就是初始化原始值。
      const [name, setName] = useState('Taylor');
      cosnt [fullName, setFullName] = useState({firstName: 'Tom', lastName: 'James'})
      const [todos, setTodos] = useState(() => createTodos()) // 用回調(diào)的形式填入 createTodos 的返回值
      
      const initArr = () => new Array(9).fill(null)
      const [list, setList] = useState(initArr) // 把函數(shù)本身當(dāng)參數(shù)傳入。如果將函數(shù)傳遞給 useState,React 僅在初始化期間調(diào)用它。當(dāng)組件重新渲染,它也不會(huì)再次運(yùn)行。
      
      // 修改state
      setAge(age => age + 1) // nextState的更新函數(shù)。只接受待定的 state 作為其唯一參數(shù),并應(yīng)返回下一個(gè)狀態(tài)。
      setName('Tom')
      setFullName({...fullName, firstName: 'Jerry'}) // 更新對(duì)象
      // 更新數(shù)組,新增項(xiàng), 修改項(xiàng), 刪除項(xiàng)。
      setTodos([
        ...todos,
        {
          id: id,
          title,
          done: false
        }
      ])
      setTodos(todos.filter(t => t.id !== todoId)) // 通過過濾返回刪除后剩余的項(xiàng)目。
      setTodos(tods.map(todo => todo.id !== nextTodo.id ? todo : nextTodo)) // 更新項(xiàng)
      
      // 調(diào)用狀態(tài)變量,每次setAge后,會(huì)觸發(fā)頁面的重新渲染。
      <div>{age} {fullName.firstName}</div>
      
    • 使用 Immer 來簡(jiǎn)化寫法

      import {useImmer} from 'use-immer'
      const [todos, updateTodos] = useImmer([{name: 'Tom', age: 18}])
      updateTods(draft => {
        const obj = draft.find(item => item.name === 'Tom')
        obj.age = 19
      })
      
      
    • 使用 key 來重置狀態(tài)

      • 渲染列表 時(shí),你經(jīng)常會(huì)遇到 key 屬性。然而,它還有另外一個(gè)用途。通過向組件傳遞不同的 key 來重置組件的狀態(tài)。

        const [count, setCount] = useState(0)
        const [version, setVersion] = useState(0)
        
        setVersion(version + 1)
        
        // count更新時(shí)視圖都會(huì)重新渲染,version變化時(shí),不管count有沒有變化視圖都會(huì)重新渲染
        <div key={version}>{count}</div>
        
  • 存儲(chǔ)前一次渲染的信息

    • 在極為罕見的情況下,在組件渲染時(shí)調(diào)用 set 函數(shù)來基于已經(jīng)渲染的值更新狀態(tài)。
  • set函數(shù)執(zhí)行時(shí)機(jī),是遇見set函數(shù)就推進(jìn)執(zhí)行隊(duì)列,要當(dāng)前作用域內(nèi)容執(zhí)行之后才會(huì)進(jìn)行執(zhí)行隊(duì)列進(jìn)行更新狀態(tài)值

    const [count, setCount] = useState(0)
    const handleClick = () => {
        // log(count) 0
        setCount(2)
        // log(count) 0
        setTimeout(() => {
            // log(count) 0
        })
    }
    // 解決辦法, 使用臨時(shí)變量
    const handleClick = () => {
        const nextCount = count + 1
        setCount(nextCount)
        // log(count) 0
        // log(nextCount) 1
    }
    
  • useReducer

  • 在組件的頂層作用域調(diào)用 useReducer 以創(chuàng)建一個(gè)用于管理狀態(tài)的 reducer。

  • 使用場(chǎng)景:批量更新 state時(shí)候使用。

  • useReducer的三個(gè)參數(shù)

    import {useReducer} from 'react'
    const [state, dispatch] = useReducer(reducer, initialArg, init?)
    
    • reducer: 用于更新 state 的純函數(shù)。參數(shù)為 state 和 action,返回值是更新后的 state。state 與 action 可以是任意合法值。
    • initialArg:用于初始化 state 的任意值。初始值的計(jì)算邏輯取決于接下來的 init 參數(shù)。
    • 可選參數(shù) init:用于計(jì)算初始值的函數(shù)。如果存在,使用 init(initialArg) 的執(zhí)行結(jié)果作為初始值,否則使用 initialArg。
  • dispatch 函數(shù): useReducer 返回的 dispatch 函數(shù)允許你更新 state 并觸發(fā)組件的重新渲染

  • 使用示例:

    import {useReducer, useState} from 'react'
    cosnt initData = [
      {id: 1, name: 'Tom', age: 18},
      {id: 2, name: 'Jerry', age: 16}
    ]
    /**
    * 聲明reducer函數(shù)
    * 兩個(gè)參數(shù),list是當(dāng)前的state, action 可以是任意合法值,包含了要操作的類型和需要跟新的數(shù)據(jù)。
    * 返回值是 更新后的 state
    */
    const stuReducer (list, action) {
      switch (action.type) {
        case 'add': { // 往數(shù)組中添加數(shù)據(jù)
          return [
            ...list,
            {id: action.id, name: action.name, age: action.age}
          ]
        }
        case 'update': { // 更新數(shù)組中一項(xiàng)的數(shù)據(jù)
          return list.map(item => item.id === action.id ? ({id: action.id, name: action.name, age: action.age}) : item)
        }
        case 'delete': { // 過濾掉要?jiǎng)h除的數(shù)據(jù)
          return list.filter(item => item.id !== aciton.id)
        }
        default: {
          throw Error ('沒有找到對(duì)應(yīng)的' + action.type)
        }
      }
    }
    
    const Demo = () => {
      /**
      * useReducer三個(gè)參數(shù)
      * 第一個(gè)參數(shù):stuReducer 用于更新 state 的純函數(shù)
      * 第二個(gè)參數(shù):initData 用于初始化 state 的任意值。
      * 補(bǔ)?。旱诙€(gè)參數(shù)initData也可以是一個(gè)函數(shù)的返回值比如formatArr(initData)。但是這樣的話每次更新state時(shí)候formatArr函數(shù)都會(huì)被調(diào)用,請(qǐng)把formatArr函數(shù)本身作為第三個(gè)參數(shù)傳入useReducer中。例如:useReducer(stuReducer, initData, formatArr)
      * 還有第三個(gè)可選參數(shù),這里省略了。是用于計(jì)算初始值的函數(shù)。
      * 
      */
      const [list, dispatch] = useReducer(stuReducer, initData)
      
      const oprateList = (type) {
        // 根據(jù)不同的type值調(diào)用reducer函數(shù)返回對(duì)應(yīng)更新后的state.
        dispatch({
          type: type,
                id: 'xx',
          name: 'xx',
          age: 99
        })
        // dispatch之后打印一下list,發(fā)現(xiàn)還是更新之前的數(shù)據(jù),這里的原理與 useState 相同。這是因?yàn)?state 的行為和快照一樣。更新 state 會(huì)使用新的值來對(duì)組件進(jìn)行重新渲染,但是不會(huì)改變當(dāng)前執(zhí)行的事件處理函數(shù)里面 state 的值。
      }
      return <div>要渲染的內(nèi)容以及操作的按鈕內(nèi)容</div>
    }
    
    
  • reducer 和初始化函數(shù)運(yùn)行了兩次

    • 嚴(yán)格模式 下 React 會(huì)調(diào)用兩次 reducer 和初始化函數(shù),但是這不應(yīng)該會(huì)破壞你的代碼邏輯。
    • 這個(gè) 僅限于開發(fā)模式 的行為可以幫助你 保持組件純粹:React 會(huì)使用其中一次調(diào)用結(jié)果并忽略另一個(gè)結(jié)果。如果你的組件、初始化函數(shù)以及 reducer 函數(shù)都是純函數(shù),這并不會(huì)影響你的邏輯。不過一旦它們存在副作用,這個(gè)額外的行為就可以幫助你發(fā)現(xiàn)它。

2. Context Hook

  • useContext

    • 向組件樹深層傳遞數(shù)據(jù)。與prop的父子組件組件傳值相比,context可跨代進(jìn)行數(shù)據(jù)傳遞。并能更新傳遞的數(shù)據(jù)。

    • 使用示例

      // createContext.ts
      // 第一步要?jiǎng)?chuàng)建context
      import {createContext} from 'react'
      const ThemeContext = createContext(null) // 也可以默認(rèn)主體色 light createContext('light') 
      export default ThemeContext
      
      // 組件中調(diào)用context
      import {useContext, useState} from 'react'
      import ThemeContext from './createContext'
      const App = () => {
        const [theme, setTheme] = useState('dark')
        
        /**
        * value='dark' 修改了 themeContext的值
        * 如果這里的 value值 不輸入,則取的是 createContext的默認(rèn)值。
        * 下面是通過使用state來初始化和更新 themeContext的值的示例。
        * <ThemeContext.Provider value={theme}><Form /></ThemeContext.Provider>
        * setTheme('light') 會(huì)更新themeContext的值
        */
        return (
          <ThemeContext.Provider value='dark'><Form /></ThemeContext.Provider>
        )
      }
      const Form = () => {
        const theme = useContext(ThemeContext)
        const className = `box-${theme}` // box-dark
        return <div className={className}></div>
      }
      
  • 補(bǔ)丁: 通過在 provider 中使用不同的值包裝樹的某個(gè)部分,可以覆蓋該部分的 context。

    <ThemeContext.Provider value="dark">
      ...
      <ThemeContext.Provider value="light">
        <Footer />
      </ThemeContext.Provider>
      ...
    </ThemeContext.Provider>
    

3. Ref Hook

  • useRef

    • 引用一個(gè)不需要渲染的值,通過ref操作 DOM,儲(chǔ)存定時(shí)器 interval ID。

      const ref = useRef(initialValue)
      # initialValue:ref 對(duì)象的 current 屬性的初始值??梢允侨我忸愋偷闹?。這個(gè)參數(shù)在首次渲染后被忽略。
      
    • 改變Ref不會(huì)觸發(fā)頁面的重新渲染

    • 使用示例

      import {useRef} from 'react'
      const MyApp = () => {
        const inputRef = useRef(null) // 引用input的元素
        const intervalRef = useRef(null)
        
        const handleClick = () => {
          const intervalRef = setInterval(() => {}, 10)
          // 清除定時(shí)器
          clearInterval(intervalRef.current)
          
          // 引用dom
          inputRef.current.focus()
        }
        
        return <>
          <input ref={inputRef} />
        </>
      }
      

4. Effect Hook

  • useEffect

    • 允許你 將組件與外部系統(tǒng)同步。

      useEffect(setup, dependencies?)     
      
      • setup:處理 Effect 的函數(shù)。setup 函數(shù)選擇性返回一個(gè) 清理(cleanup) 函數(shù)。當(dāng)組件被添加到 DOM 的時(shí)候,React 將運(yùn)行 setup 函數(shù)。在每次依賴項(xiàng)變更重新渲染后,React 將首先使用舊值運(yùn)行 cleanup 函數(shù)(如果你提供了該函數(shù)),然后使用新值運(yùn)行 setup 函數(shù)。在組件從 DOM 中移除后,React 將最后一次運(yùn)行 cleanup 函數(shù)。

      • 可選 dependenciessetup 代碼中引用的所有響應(yīng)式值的列表。<font color='red'>響應(yīng)式值包括 props、state 以及所有直接在組件內(nèi)部聲明的變量和函數(shù)</font>。如果你的代碼檢查工具 配置了 React,那么它將驗(yàn)證是否每個(gè)響應(yīng)式值都被正確地指定為一個(gè)依賴項(xiàng)。依賴項(xiàng)列表的元素?cái)?shù)量必須是固定的,并且必須像 [dep1, dep2, dep3] 這樣內(nèi)聯(lián)編寫。React 將使用 Object.is 來比較每個(gè)依賴項(xiàng)和它先前的值。如果省略此參數(shù),則在每次重新渲染組件之后,將重新運(yùn)行 Effect 函數(shù)。

        • 如果指定了依賴項(xiàng),則 Effect 在 初始渲染后以及依賴項(xiàng)變更的重新渲染后 運(yùn)行。

          useEffect(() => {
            // ...
          }, [a, b]); // 如果 a 或 b 不同則會(huì)再次運(yùn)行
          
  - 如果你的 Effect 確實(shí)沒有使用任何響應(yīng)式值,則它僅在 **初始渲染后** 運(yùn)行。

    ```jsx
    useEffect(() => {
      // ...
    }, []); // 不會(huì)再次運(yùn)行(開發(fā)環(huán)境下除外)
    ```

    

  - 如果完全不傳遞依賴數(shù)組,則 Effect 會(huì)在組件的 **每次單獨(dú)渲染(和重新渲染)之后** 運(yùn)行。

    ```jsx
    useEffect(() => {
      // ...
    }); // 總是再次運(yùn)行
    ```
  • useLayoutEffect

    • useLayoutEffectuseEffect 的一個(gè)版本,在瀏覽器重新繪制屏幕之前觸發(fā)。 可能會(huì)影響性能。<font color="red">謹(jǐn)慎使用</font>
    • 在瀏覽器重新繪制屏幕前計(jì)算布局。
  • useInsertionEffect

    • 是為 CSS-in-JS 庫的作者特意打造的。除非你正在使用 CSS-in-JS 庫并且需要注入樣式,否則你應(yīng)該使用 useEffect 或者 useLayoutEffect

5. 性能 Hook

  • useMemo
    • 它在每次重新渲染的時(shí)候能夠緩存計(jì)算的結(jié)果。
  • useCallback
    • 是一個(gè)允許你在多次渲染中緩存函數(shù)的 React Hook。
  • useTransition
    • 是一個(gè)幫助你在不阻塞 UI 的情況下更新狀態(tài)的 React Hook。
    • 它允許你在某些狀態(tài)更新時(shí)實(shí)現(xiàn)過渡效果,即這些更新可以有不同的優(yōu)先級(jí),從而可以延遲一些不那么重要的更新,以便更快速地響應(yīng)更關(guān)鍵的用戶交互。這個(gè)Hook的主要作用是在狀態(tài)更新時(shí),提供了一種方式來“標(biāo)記”某些更新為“過渡”,這意味著這些更新可能會(huì)延遲,以便React可以先處理其他更高優(yōu)先級(jí)的更新,如用戶的點(diǎn)擊事件。
    • 常用于優(yōu)化視圖切換時(shí)的用戶體驗(yàn)。例如,當(dāng)某個(gè)組件的渲染特別耗時(shí),如Movie組件,如果在渲染該組件期間頁面的UI被阻塞,用戶會(huì)感覺頁面卡頓。通過使用useTransition,可以優(yōu)化這種情況下的用戶體驗(yàn)。此外,useTransition還可以幫助你控制過渡效果的持續(xù)時(shí)間和延遲時(shí)間等參數(shù),使得用戶可以看到過渡效果,而不是直接看到新的組件,從而提高用戶體驗(yàn)。
  • useDeferredValue
    • 可以讓你延遲更新 UI 的某些部分。
    • 可以將 useDeferredValue 作為性能優(yōu)化的手段。當(dāng)你的 UI 某個(gè)部分重新渲染很慢、沒有簡(jiǎn)單的優(yōu)化方法,同時(shí)你又希望避免它阻塞其他 UI 的渲染時(shí),使用 useDeferredValue 很有幫助。

6. 資源 Hook

  • use 允許讀取像 Promisecontext 這樣的資源的值。僅在 Canary 與 experimental 渠道中可用

7. 其他 Hook

  • 使用 useDebugValue 自定義 React 開發(fā)者工具為自定義 Hook 添加的標(biāo)簽。
  • 使用 useId 將唯一的 ID 與組件相關(guān)聯(lián),其通常與可訪問性 API 一起使用。
  • 使用 useSyncExternalStore 訂閱外部 store。

8. 自定義 Hook

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

相關(guān)閱讀更多精彩內(nèi)容

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