react hooks 萬字總結(jié),帶你夯實(shí)基礎(chǔ)

react hooks

react hooks 萬字總結(jié),帶你夯實(shí)基礎(chǔ)

前言

自己在掘金上看了也看了很多關(guān)于hooks的文章未舟,感覺都講得不是很詳細(xì)术瓮。而且也有很多的水文雕擂。最近自己打算重學(xué)react冰更,系統(tǒng)性的再把hooks給學(xué)習(xí)一遍产徊。

Hooks is what?

  • react-hooks是react16.8以后冬殃,react新增的鉤子API囚痴,它可以讓你在不編寫 class 的情況下使用 state 以及其他的 React 特性.
  • Hook是一些可以讓你在函數(shù)組件里“鉤入” React state 及生命周期等特性的函數(shù)。

why use Hooks审葬?

類組件的缺點(diǎn):(來自官網(wǎng)動機(jī)

  • 在組件之間復(fù)用狀態(tài)邏輯很難

  • 復(fù)雜組件變得難以理解

  • 難以理解的 class

你必須去理解 JavaScript 中 this 的工作方式深滚,這與其他語言存在巨大差異。還不能忘記綁定事件處理器涣觉。沒有穩(wěn)定的語法提案痴荐,代碼非常冗余。

hooks的出現(xiàn)官册,解決了上面的問題生兆。另外,還有一些其他的優(yōu)點(diǎn)

  • 增加代碼的可復(fù)用性膝宁,邏輯性鸦难,彌補(bǔ)無狀態(tài)組件沒有生命周期根吁,沒有數(shù)據(jù)管理狀態(tài)state的缺陷
  • react-hooks思想更趨近于函數(shù)式編程。用函數(shù)聲明方式代替class聲明方式合蔽,雖說class也是es6構(gòu)造函數(shù)語法糖击敌,但是react-hooks寫起來函數(shù)即是組件,無疑也提高代碼的開發(fā)效率(無需像class聲明組件那樣寫聲明周期拴事,寫生命周期render函數(shù)等)

Hooks沒有破壞性改動

  • 完全可選的沃斤。 你無需重寫任何已有代碼就可以在一些組件中嘗試 Hook。但是如果你不想刃宵,你不必現(xiàn)在就去學(xué)習(xí)或使用 Hook衡瓶。
  • 100% 向后兼容的。 Hook 不包含任何破壞性改動牲证。
  • 現(xiàn)在可用哮针。 Hook 已發(fā)布于 v16.8.0。

使用Hooks的規(guī)則

1. 只在最頂層使用 Hook,不要在循環(huán)从隆,條件或嵌套函數(shù)中調(diào)用 Hook

確背夏欤總是在你的 React 函數(shù)的最頂層調(diào)用他們。遵守這條規(guī)則键闺,你就能確保 Hook 在每一次渲染中都按照同樣的順序被調(diào)用。這讓 React 能夠在多次的 useStateuseEffect 調(diào)用之間保持 hook 狀態(tài)的正確澈驼。

2. 只在 React 函數(shù)中調(diào)用 Hook

不要在普通的 JavaScript 函數(shù)中調(diào)用 Hook,你可以:

  • ? 在 React 的函數(shù)組件中調(diào)用 Hook
  • ? 在自定義 Hook 中調(diào)用其他 Hook

至于為什么會有這些規(guī)則辛燥,如果你感興趣,請參考Hook 規(guī)則

useState

const [state, setState] = useState(initialState)

  • useState 有一個參數(shù)(initialState 可以是一個函數(shù)缝其,返回一個值挎塌,但一般都不會這么用),該參數(shù)可以為任意數(shù)據(jù)類型内边,一般用作默認(rèn)值.
  • useState 返回值為一個數(shù)組榴都,數(shù)組的第一個參數(shù)為我們需要使用的 state,第二個參數(shù)為一個改變state的函數(shù)(功能和this.setState一樣)

來看一個計(jì)時器的案例

import React,{useState} from "react";
function Example() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  );
}
export default Example;
復(fù)制代碼
  • 第一行: 引入 React 中的 useState Hook漠其。它讓我們在函數(shù)組件中存儲內(nèi)部 state嘴高。
  • 第三行:Example 組件內(nèi)部,我們通過調(diào)用 useState Hook 聲明了一個新的 state 變量和屎。它返回一對值給到我們命名的變量上拴驮。我們把變量命名為 count,因?yàn)樗鎯Φ氖屈c(diǎn)擊次數(shù)柴信。我們通過傳 0 作為 useState 唯一的參數(shù)來將其初始化為 0套啤。第二個返回的值本身就是一個函數(shù)。它讓我們可以更新 count 的值随常,所以我們叫它 setCount潜沦。
  • 第七行: 當(dāng)用戶點(diǎn)擊按鈕后萄涯,我們傳遞一個新的值給 setCount。React 會重新渲染 Example 組件唆鸡,并把最新的 count 傳給它窃判。

使用多個state 變量

 // 聲明多個 state 變量
  const [age, setAge] = useState(42);
  const [fruit, setFruit] = useState('banana');
  const [todos, setTodos] = useState([{ text: '學(xué)習(xí) Hook' }]);
復(fù)制代碼

不必使用多個 state 變量。State 變量可以很好地存儲對象和數(shù)組喇闸,因此袄琳,你仍然可以將相關(guān)數(shù)據(jù)分為一組。

更新State

import React,{useState} from "react";
function Example() {
  const [count, setCount] = useState(0);
  const [person, setPerson] = useState({name:'jimmy',age:22});
  return (
    <div>
      <p>name {person.name} </p>
      // 如果新的 state 需要通過使用先前的 state 計(jì)算得出燃乍,那么可以將回調(diào)函數(shù)當(dāng)做參數(shù)傳遞給 setState唆樊。
      // 該回調(diào)函數(shù)將接收先前的 state,并返回一個更新后的值刻蟹。
      <button onClick={() => setCount(count=>count+1)}>Click me</button>
      <button onClick={() => setPerson({name:'chimmy'})}>Click me</button>
    </div>
  );
}
export default Example;
復(fù)制代碼

setPerson更新person時逗旁,不像 class 中的 this.setState,更新 state 變量總是替換它而不是合并它舆瘪。上例中的person為{name:'chimmy'} 而不是{name:'chimmy',age:22}

useEffect

Effect Hook 可以讓你在函數(shù)組件中執(zhí)行副作用(數(shù)據(jù)獲取片效,設(shè)置訂閱以及手動更改 React 組件中的 DOM 都屬于副作用)操作

useEffect(fn, array)

useEffect在初次完成渲染之后都會執(zhí)行一次, 配合第二個參數(shù)可以模擬類的一些生命周期。

如果你熟悉 React class 的生命周期函數(shù)英古,你可以把 useEffect Hook 看做 componentDidMount``componentDidUpdatecomponentWillUnmount 這三個函數(shù)的組合淀衣。

useEffect 實(shí)現(xiàn)componentDidMount

如果第二個參數(shù)為空數(shù)組,useEffect相當(dāng)于類組件里面componentDidMount召调。

import React, { useState, useEffect } from "react";
function Example() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    console.log("我只會在組件初次掛載完成后執(zhí)行");
  }, []);
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  );
}
export default Example;
復(fù)制代碼

頁面渲染完成后膨桥,會執(zhí)行一次useEffect。打印“我只會在組件初次掛載完成后執(zhí)行”唠叛,當(dāng)點(diǎn)擊按鈕改變了state只嚣,頁面重新渲染后,useEffect不會執(zhí)行艺沼。

useEffect 實(shí)現(xiàn)componentDidUpdate

如果不傳第二個參數(shù)册舞,useEffect 會在初次渲染和每次更新時,都會執(zhí)行障般。

import React, { useState, useEffect } from "react";
function Example() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    console.log("我會在初次組件掛載完成后以及重新渲染時執(zhí)行");
  });
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  );
}
export default Example;
復(fù)制代碼

初次渲染時调鲸,會執(zhí)行一次useEffect,打印出“我會在初次組件掛載完成后以及重新渲染時執(zhí)行”剩拢。 當(dāng)點(diǎn)擊按鈕時线得,改變了state,頁面重新渲染徐伐,useEffect都會執(zhí)行贯钩,打印出“我會在初次組件掛載完成后以及重新渲染時執(zhí)行”。

useEffect 實(shí)現(xiàn)componentWillUnmount

effect 返回一個函數(shù),React 將會在執(zhí)行清除操作時調(diào)用它角雷。

useEffect(() => {
    console.log("訂閱一些事件");
    return () => {
      console.log("執(zhí)行清除操作")
    }
  },[]);
復(fù)制代碼

注意:這里不只是組件銷毀時才會打印“執(zhí)行清除操作”祸穷,每次重新渲染時也都會執(zhí)行。至于原因勺三,我覺得官網(wǎng)解釋的很清楚雷滚,請參考 解釋: 為什么每次更新的時候都要運(yùn)行 Effect

控制useEffect的執(zhí)行

import React, { useState, useEffect } from "react";
function Example() {
  const [count, setCount] = useState(0);
  const [number, setNumber] = useState(1);
  useEffect(() => {
    console.log("我只會在cout變化時執(zhí)行");
  }, [count]);
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click cout</button>
      <button onClick={() => setNumber(number + 1)}>Click number</button>
    </div>
  );
}
export default Example;
復(fù)制代碼

上面的例子,在點(diǎn)擊 click cout按鈕時吗坚,才會打印“我只會在cout變化時執(zhí)行”祈远。 因?yàn)閡seEffect 的第二個參數(shù)的數(shù)組里面的依賴是cout,所以商源,只有cout發(fā)生改變時车份,useEffect 才會執(zhí)行。如果數(shù)組中有多個元素牡彻,即使只有一個元素發(fā)生變化扫沼,React 也會執(zhí)行 effect。

使用多個 Effect 實(shí)現(xiàn)關(guān)注點(diǎn)分離

使用 Hook 其中一個目的就是要解決 class 中生命周期函數(shù)經(jīng)常包含不相關(guān)的邏輯庄吼,但又把相關(guān)邏輯分離到了幾個不同方法中的問題缎除。

import React, { useState, useEffect } from "react";
function Example() {
  useEffect(() => {
    // 邏輯一
  });
  useEffect(() => {
    // 邏輯二
  });
   useEffect(() => {
    // 邏輯三
  });
  return (
    <div>
      useEffect的使用
    </div>
  );
}
export default Example;
復(fù)制代碼

Hook 允許我們按照代碼的用途分離他們, 而不是像生命周期函數(shù)那樣总寻。React 將按照 effect 聲明的順序依次調(diào)用組件中的每一個 effect器罐。

useEffect中使用異步函數(shù)

useEffect是不能直接用 async await 語法糖的

/* 錯誤用法 ,effect不支持直接 async await*/
 useEffect(async ()=>{
        /* 請求數(shù)據(jù) */
      const res = await getData()
 },[])
復(fù)制代碼

useEffect 的回調(diào)參數(shù)返回的是一個清除副作用的 clean-up 函數(shù)废菱。因此無法返回 Promise技矮,更無法使用 async/await

那我們應(yīng)該如何讓useEffect支持async/await呢?

方法一(推薦)

const App = () => {
  useEffect(() => {
    (async function getDatas() {
      await getData();
    })();
  }, []);
  return <div></div>;
};
復(fù)制代碼

方法二

  useEffect(() => {
    const getDatas = async () => {
      const data = await getData();
      setData(data);
    };
    getDatas();
  }, []);
復(fù)制代碼

useEffect 做了什么

通過使用這個 Hook殊轴,你可以告訴 React 組件需要在渲染后執(zhí)行某些操作。React 會保存你傳遞的函數(shù)(我們將它稱之為 “effect”)袒炉,并且在執(zhí)行 DOM 更新之后調(diào)用它旁理。

為什么在組件內(nèi)部調(diào)用 useEffect

useEffect 放在組件內(nèi)部讓我們可以在 effect 中直接訪問 count state 變量(或其他 props)我磁。我們不需要特殊的 API 來讀取它 —— 它已經(jīng)保存在函數(shù)作用域中孽文。Hook 使用了 JavaScript 的閉包機(jī)制,而不用在 JavaScript 已經(jīng)提供了解決方案的情況下夺艰,還引入特定的 React API芋哭。

useContext

概念

const value = useContext(MyContext);

接收一個 context 對象(React.createContext 的返回值)并返回該 context 的當(dāng)前值。當(dāng)組件上層最近的 <MyContext.Provider> 更新時郁副,該 Hook 會觸發(fā)重渲染减牺,并使用最新傳遞給 MyContext provider 的 context value 值。即使祖先使用 React.memoshouldComponentUpdate,也會在組件本身使用 useContext 時重新渲染拔疚。

別忘記 useContext 的參數(shù)必須是 context 對象本身

  • 正確: useContext(MyContext)
  • 錯誤: useContext(MyContext.Consumer)
  • 錯誤: useContext(MyContext.Provider)

示例

index.js

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
// 創(chuàng)建兩個context
export const UserContext = React.createContext();
export const TokenContext = React.createContext();
ReactDOM.render(
  <UserContext.Provider value={{ id: 1, name: "chimmy", age: "20" }}>
    <TokenContext.Provider value="我是token">
      <App />
    </TokenContext.Provider>
  </UserContext.Provider>,
  document.getElementById("root")
);
復(fù)制代碼

app.js

import React, { useContext } from "react";
import { UserContext, TokenContext } from "./index";

function Example() {
  let user = useContext(UserContext);
  let token = useContext(TokenContext);
  console.log("UserContext", user);
  console.log("TokenContext", token);
  return (
    <div>
      name:{user?.name},age:{user?.age}
    </div>
  );
}
export default Example;
復(fù)制代碼

打印的值如下

image.png

提示

如果你在接觸 Hook 前已經(jīng)對 context API 比較熟悉肥隆,那應(yīng)該可以理解,useContext(MyContext) 相當(dāng)于 class 組件中的 static contextType = MyContext 或者 <MyContext.Consumer>稚失。

useContext(MyContext) 只是讓你能夠讀取 context 的值以及訂閱 context 的變化栋艳。你仍然需要在上層組件樹中使用 <MyContext.Provider> 來為下層組件提供 context。

useReducer

概念

const [state, dispatch] = useReducer(reducer, initialArg, init);

useState 的替代方案句各。它接收一個形如 (state, action) => newState 的 reducer吸占,并返回當(dāng)前的 state 以及與其配套的 dispatch 方法。(如果你熟悉 Redux 的話凿宾,就已經(jīng)知道它如何工作了矾屯。)

在某些場景下,useReducer 會比 useState 更適用菌湃,例如 state 邏輯較復(fù)雜且包含多個子值问拘,或者下一個 state 依賴于之前的 state 等。并且惧所,使用 useReducer 還能給那些會觸發(fā)深更新的組件做性能優(yōu)化骤坐,因?yàn)槟憧梢韵蜃咏M件傳遞 dispatch 而不是回調(diào)函數(shù)

注意點(diǎn)

React 會確保 dispatch 函數(shù)的標(biāo)識是穩(wěn)定的,并且不會在組件重新渲染時改變下愈。這就是為什么可以安全地從 useEffectuseCallback 的依賴列表中省略 dispatch纽绍。

示例

import React, { useReducer } from "react";
export default function Home() {
  function reducer(state, action) {
    switch (action.type) {
      case "increment":
        return { ...state, counter: state.counter + 1 };
      case "decrement":
        return { ...state, counter: state.counter - 1 };
      default:
        return state;
    }
  }
  const [state, dispatch] = useReducer(reducer, { counter: 0 });
  return (
    <div>
      <h2>Home當(dāng)前計(jì)數(shù): {state.counter}</h2>
      <button onClick={(e) => dispatch({ type: "increment" })}>+1</button>
      <button onClick={(e) => dispatch({ type: "decrement" })}>-1</button>
    </div>
  );
}
復(fù)制代碼

useCallback

概念

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);
復(fù)制代碼

返回一個 [memoized]回調(diào)函數(shù)。

把內(nèi)聯(lián)回調(diào)函數(shù)及依賴項(xiàng)數(shù)組作為參數(shù)傳入 useCallback势似,它將返回該回調(diào)函數(shù)的 memoized 版本拌夏,該回調(diào)函數(shù)僅在某個依賴項(xiàng)改變時才會更新。當(dāng)你把回調(diào)函數(shù)傳遞給經(jīng)過優(yōu)化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate)的子組件時履因,它將非常有用障簿。

示例

import React, { useState } from "react";
// 子組件
function Childs(props) {
  console.log("子組件渲染了");
  return (
    <>
      <button onClick={props.onClick}>改標(biāo)題</button>
      <h1>{props.name}</h1>
    </>
  );
}
const Child = React.memo(Childs);
function App() {
  const [title, setTitle] = useState("這是一個 title");
  const [subtitle, setSubtitle] = useState("我是一個副標(biāo)題");
  const callback = () => {
    setTitle("標(biāo)題改變了");
  };
  return (
    <div className="App">
      <h1>{title}</h1>
      <h2>{subtitle}</h2>
      <button onClick={() => setSubtitle("副標(biāo)題改變了")}>改副標(biāo)題</button>
      <Child onClick={callback} name="桃桃" />
    </div>
  );
}
復(fù)制代碼

執(zhí)行結(jié)果如下圖


image.png

當(dāng)我點(diǎn)擊改副標(biāo)題這個 button 之后,副標(biāo)題會變?yōu)椤父睒?biāo)題改變了」栅迄,并且控制臺會再次打印出子組件渲染了站故,這就證明了子組件重新渲染了,但是子組件沒有任何變化毅舆,那么這次 Child 組件的重新渲染就是多余的西篓,那么如何避免掉這個多余的渲染呢?

找原因

我們在解決問題的之前憋活,首先要知道這個問題是什么原因?qū)е碌模?/strong>

咱們來分析岂津,一個組件重新重新渲染,一般三種情況:

  1. 要么是組件自己的狀態(tài)改變
  2. 要么是父組件重新渲染悦即,導(dǎo)致子組件重新渲染吮成,但是父組件的 props 沒有改變
  3. 要么是父組件重新渲染橱乱,導(dǎo)致子組件重新渲染,但是父組件傳遞的 props 改變

接下來用排除法查出是什么原因?qū)е碌模?/p>

第一種很明顯就排除了赁豆,當(dāng)點(diǎn)擊改副標(biāo)題 的時候并沒有去改變 Child 組件的狀態(tài)仅醇;

第二種情況,我們這個時候用 React.memo 來解決了這個問題魔种,所以這種情況也排除析二。

那么就是第三種情況了,當(dāng)父組件重新渲染的時候节预,傳遞給子組件的 props 發(fā)生了改變叶摄,再看傳遞給 Child 組件的就兩個屬性,一個是 name安拟,一個是 onClick 蛤吓,name 是傳遞的常量,不會變糠赦,變的就是 onClick 了会傲,為什么傳遞給 onClick 的 callback 函數(shù)會發(fā)生改變呢?其實(shí)在函數(shù)式組件里每次重新渲染拙泽,函數(shù)組件都會重頭開始重新執(zhí)行淌山,那么這兩次創(chuàng)建的 callback 函數(shù)肯定發(fā)生了改變,所以導(dǎo)致了子組件重新渲染顾瞻。

用useCallback解決問題

const callback = () => {
  doSomething(a, b);
}
const memoizedCallback = useCallback(callback, [a, b])
復(fù)制代碼

把函數(shù)以及依賴項(xiàng)作為參數(shù)傳入 useCallback泼疑,它將返回該回調(diào)函數(shù)的 memoized 版本,這個 memoizedCallback 只有在依賴項(xiàng)有變化的時候才會更新荷荤。

那么只需這樣將傳給Child組件callback函數(shù)的改造一下就OK了

const callback = () => { setTitle("標(biāo)題改變了"); };
// 通過 useCallback 進(jìn)行記憶 callback退渗,并將記憶的 callback 傳遞給 Child
<Child onClick={useCallback(callback, [])} name="桃桃" />
復(fù)制代碼

這樣我們就可以看到只會在首次渲染的時候打印出子組件渲染了,當(dāng)點(diǎn)擊改副標(biāo)題和改標(biāo)題的時候是不會打印子組件渲染了的蕴纳。

useMemo

概念

const cacheSomething = useMemo(create,deps)

  • create:第一個參數(shù)為一個函數(shù)会油,函數(shù)的返回值作為緩存值。
  • deps: 第二個參數(shù)為一個數(shù)組古毛,存放當(dāng)前 useMemo 的依賴項(xiàng)钞啸,在函數(shù)組件下一次執(zhí)行的時候,會對比 deps 依賴項(xiàng)里面的狀態(tài)喇潘,是否有改變,如果有改變重新執(zhí)行 create 梭稚,得到新的緩存值颖低。
  • cacheSomething:返回值,執(zhí)行 create 的返回值弧烤。如果 deps 中有依賴項(xiàng)改變忱屑,返回的重新執(zhí)行 create 產(chǎn)生的值蹬敲,否則取上一次緩存值。

useMemo原理

useMemo 會記錄上一次執(zhí)行 create 的返回值莺戒,并把它綁定在函數(shù)組件對應(yīng)的 fiber 對象上伴嗡,只要組件不銷毀,緩存值就一直存在从铲,但是 deps 中如果有一項(xiàng)改變瘪校,就會重新執(zhí)行 create ,返回值作為新的值記錄到 fiber 對象上名段。

示例

function Child(){
    console.log("子組件渲染了")
    return <div>Child</div> 
}
const Child = memo(Child)
function APP(){
    const [count, setCount] = useState(0);
    const userInfo = {
      age: count,
      name: 'jimmy'
    }
    return <Child userInfo={userInfo}>
}

復(fù)制代碼

當(dāng)函數(shù)組件重新render時阱扬,userInfo每次都將是一個新的對象,無論 count 發(fā)生改變沒伸辟,都會導(dǎo)致 Child組件的重新渲染麻惶。

而下面的則會在 count 改變后才會返回新的對象。

function Child(){
    console.log("子組件渲染了")
    return <div>Child</div> 
}
function APP(){
    const [count, setCount] = useState(0);
    const userInfo = useMemo(() => {
      return {
        name: "jimmy",
        age: count
      };
    }, [count]);
    return <Child userInfo={userInfo}>
}
復(fù)制代碼

實(shí)際上 useMemo 的作用不止于此信夫,根據(jù)官方文檔內(nèi)介紹:以把一些昂貴的計(jì)算邏輯放到 useMemo 中窃蹋,只有當(dāng)依賴值發(fā)生改變的時候才去更新。

import React, {useState, useMemo} from 'react';

// 計(jì)算和的函數(shù)静稻,開銷較大
function calcNumber(count) {
  console.log("calcNumber重新計(jì)算");
  let total = 0;
  for (let i = 1; i <= count; i++) {
    total += i;
  }
  return total;
}
export default function MemoHookDemo01() {
  const [count, setCount] = useState(100000);
  const [show, setShow] = useState(true);
  const total = useMemo(() => {
    return calcNumber(count);
  }, [count]);
  return (
    <div>
      <h2>計(jì)算數(shù)字的和: {total}</h2>
      <button onClick={e => setCount(count + 1)}>+1</button>
      <button onClick={e => setShow(!show)}>show切換</button>
    </div>
  )
}
復(fù)制代碼

當(dāng)我們?nèi)c(diǎn)擊 show切換按鈕時警没,calcNumber這個計(jì)算和的函數(shù)并不會出現(xiàn)渲染了.只有count 發(fā)生改變時,才會出現(xiàn)計(jì)算.

useCallback 和 useMemo 總結(jié)

簡單理解呢 useCallback 與 useMemo 一個緩存的是函數(shù)姊扔,一個緩存的是函數(shù)的返回的結(jié)果惠奸。useCallback 是來優(yōu)化子組件的,防止子組件的重復(fù)渲染恰梢。useMemo 可以優(yōu)化當(dāng)前組件也可以優(yōu)化子組件佛南,優(yōu)化當(dāng)前組件主要是通過 memoize 來將一些復(fù)雜的計(jì)算邏輯進(jìn)行緩存。當(dāng)然如果只是進(jìn)行一些簡單的計(jì)算也沒必要使用 useMemo嵌言。

我們可以將 useMemo 的返回值定義為返回一個函數(shù)這樣就可以變通的實(shí)現(xiàn)了 useCallback嗅回。useCallback(fn, deps) 相當(dāng)于 useMemo(() => fn, deps)

useRef

const refContainer = useRef(initialValue);

useRef 返回一個可變的 ref 對象摧茴,其 .current 屬性被初始化為傳入的參數(shù)(initialValue)绵载。返回的 ref 對象在組件的整個生命周期內(nèi)保持不變

useRef 獲取dom

useRef,它有一個參數(shù)可以作為緩存數(shù)據(jù)的初始值,返回值可以被dom元素ref標(biāo)記苛白,可以獲取被標(biāo)記的元素節(jié)點(diǎn).

import React, { useRef } from "react";
function Example() {
  const divRef = useRef();
  function changeDOM() {
    // 獲取整個div
    console.log("整個div", divRef.current);
    // 獲取div的class
    console.log("div的class", divRef.current.className);
    // 獲取div自定義屬性
    console.log("div自定義屬性", divRef.current.getAttribute("data-clj"));
  }
  return (
    <div>
      <div className="div-class" data-clj="我是div的自定義屬性" ref={divRef}>
        我是div
      </div>
      <button onClick={(e) => changeDOM()}>獲取DOM</button>
    </div>
  );
}
export default Example;
復(fù)制代碼
image.png

useRef 緩存數(shù)據(jù)

useRef還有一個很重要的作用就是緩存數(shù)據(jù)娃豹,我們知道usestate ,useReducer 是可以保存當(dāng)前的數(shù)據(jù)源的,但是如果它們更新數(shù)據(jù)源的函數(shù)執(zhí)行必定會帶來整個組件從新執(zhí)行到渲染购裙,如果在函數(shù)組件內(nèi)部聲明變量懂版,則下一次更新也會重置,如果我們想要悄悄的保存數(shù)據(jù)躏率,而又不想觸發(fā)函數(shù)的更新躯畴,那么useRef是一個很棒的選擇民鼓。

下面舉一個,每次換成state 上一次值的例子

import React, { useRef, useState, useEffect } from "react";
function Example() {
  const [count, setCount] = useState(0);

  const numRef = useRef(count);

  useEffect(() => {
    numRef.current = count;
  }, [count]);

  return (
    <div>
      <h2>count上一次的值: {numRef.current}</h2>
      <h2>count這一次的值: {count}</h2>
      <button onClick={(e) => setCount(count + 10)}>+10</button>
    </div>
  );
}
export default Example;
復(fù)制代碼

當(dāng) ref 對象內(nèi)容發(fā)生變化時蓬抄,useRef不會通知你丰嘉。變更 .current 屬性不會引發(fā)組件重新渲染。所以嚷缭,上面的例子中雖然numRef.current的值饮亏,已經(jīng)改變了,但是頁面上還是顯示的上一次的值峭状,重新更新時克滴,才會顯示上一次更新的值。

寫在最后

文章出處:https://juejin.cn/post/6993139082054336548

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末优床,一起剝皮案震驚了整個濱河市劝赔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌胆敞,老刑警劉巖着帽,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異移层,居然都是意外死亡仍翰,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門观话,熙熙樓的掌柜王于貴愁眉苦臉地迎上來予借,“玉大人,你說我怎么就攤上這事频蛔×槠龋” “怎么了?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵晦溪,是天一觀的道長瀑粥。 經(jīng)常有香客問我,道長三圆,這世上最難降的妖魔是什么狞换? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮舟肉,結(jié)果婚禮上修噪,老公的妹妹穿的比我還像新娘。我一直安慰自己路媚,他們只是感情好割按,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著磷籍,像睡著了一般适荣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上院领,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天弛矛,我揣著相機(jī)與錄音,去河邊找鬼比然。 笑死丈氓,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的强法。 我是一名探鬼主播万俗,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼饮怯!你這毒婦竟也來了闰歪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蓖墅,失蹤者是張志新(化名)和其女友劉穎库倘,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體论矾,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡教翩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了贪壳。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片饱亿。...
    茶點(diǎn)故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖闰靴,靈堂內(nèi)的尸體忽然破棺而出彪笼,到底是詐尸還是另有隱情肺蔚,我是刑警寧澤持际,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站吩屹,受9級特大地震影響膘掰,放射性物質(zhì)發(fā)生泄漏章姓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一识埋、第九天 我趴在偏房一處隱蔽的房頂上張望凡伊。 院中可真熱鬧,春花似錦窒舟、人聲如沸系忙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽银还。三九已至风宁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蛹疯,已是汗流浹背戒财。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留捺弦,地道東北人饮寞。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像列吼,于是被迫代替她去往敵國和親幽崩。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容