詳解 useCallback & useMemo

前言

閱讀本文章須要對 React hooksuseStateuseEffect 有基礎的瞭解。個人這篇文章內有大體介紹 在 React 項目中全量使用 Hooksjavascript

useCallback

useCallback 的做用

官方文檔:html

Pass an inline callback and an array of dependencies. useCallback will return a memoized version of the callback that only changes if one of the dependencies has changed.java

簡單來講就是返回一個函數,只有在依賴項發生變化的時候纔會更新(返回一個新的函數)。react

useCallback 的應用

在線代碼: Code Sandboxapi

import React, { useState, useCallback } from 'react';
import Button from './Button';

export default function App() {
  const [count1, setCount1] = useState(0);
  const [count2, setCount2] = useState(0);

  const handleClickButton1 = () => {
    setCount1(count1 + 1);
  };

  const handleClickButton2 = useCallback(() => {
    setCount2(count2 + 1);
  }, [count2]);

  return (
    <div> <div> <Button onClickButton={handleClickButton1}>Button1</Button> </div> <div> <Button onClickButton={handleClickButton2}>Button2</Button> </div> </div>
  );
}
複製代碼
// Button.jsx
import React from 'react';

const Button = ({ onClickButton, children }) => {
  return (
    <> <button onClick={onClickButton}>{children}</button> <span>{Math.random()}</span> </> ); }; export default React.memo(Button); 複製代碼

在案例中能夠點擊 Button1 和 Button2 兩個按鈕來查看效果,點擊 Button1 的時候只會更新 Button1 後面的內容,點擊 Button2 會將兩個按鈕後的內容都更新。這就表示我在點擊 Button2 的時候致使了兩個按鈕內都從新渲染了。數組

這裏或許會注意到 React.memo 這個方法,此方法內會對 props 作一個淺層比較,若是若是 props 沒有發生改變,則不會從新渲染此組件。緩存

const a = () => {};
const b = () => {};
a === b; // false
複製代碼

上述代碼能夠看到咱們兩個同樣的函數倒是不相等的(這是個廢話,我相信能看到這的人都知道,因此不作解釋了)。bash

const [count1, setCount1] = useState(0);
// ...
const handleClickButton1 = () => {
  setCount1(count1 + 1);
};
// ...
return <Button onClickButton={handleClickButton1}>Button1</Button>
複製代碼

回頭再看上面的 Button 組件都須要一個 onClickButton 的 props ,儘管組件內部有用 React.memo 來作優化,可是咱們聲明的 handleClickButton1 是直接定義了一個方法,這也就致使只要是父組件從新渲染(狀態或者props更新)就會致使這裏聲明出一個新的方法,新的方法和舊的方法儘管長的同樣,可是依舊是兩個不一樣的對象,React.memo 對比後發現對象 props 改變,就從新渲染了。dom

const handleClickButton2 = useCallback(() => {
  setCount2(count2 + 1);
}, [count2]);
複製代碼

上述代碼咱們的方法使用 useCallback 包裝了一層,而且後面還傳入了一個 [count2] 變量,這裏 useCallback 就會根據 count2 是否發生變化,從而決定是否返回一個新的函數,函數內部做用域也隨之更新。函數

因爲咱們的這個方法只依賴了 count2 這個變量,並且 count2 只在點擊 Button2 後纔會更新 handleClickButton2,因此就致使了咱們點擊 Button1 不從新渲染 Button2 的內容。

注意點

import React, { useState, useCallback } from 'react';
import Button from './Button';

export default function App() {
  const [count2, setCount2] = useState(0);

  const handleClickButton2 = useCallback(() => {
    setCount2(count2 + 1);
  }, []);

  return (
    <Button count={count2} onClickButton={handleClickButton2} >Button2</Button>
  );
}
複製代碼

咱們調整了一下代碼,將 useCallback 依賴的第二個參數變成了一個空的數組,這也就意味着這個方法沒有依賴值,將不會被更新。且因爲 JS 的靜態做用域致使此函數內 count2 永遠都 0

能夠點擊屢次 Button2 查看變化,會發現 Button2 後面的值只會改變一次。由於上述函數內的 count2 永遠都是 0,就意味着每次都是 0 + 1,Button 所接受的 count props,也只會從 0 變成 1且一直都將是 1,並且 handleClickButton2 也因沒有依賴項不會返回新的方法,就致使 Button 只會因 count 改變而更新一次後就不會被從新渲染。

useMemo

useMemo 的做用

官方文檔:

Pass a 「create」 function and an array of dependencies. useMemo will only recompute the memoized value when one of the dependencies has changed.

簡單來講就是傳遞一個建立函數和依賴項,建立函數會須要返回一個值,只有在依賴項發生改變的時候,纔會從新調用此函數,返回一個新的依賴值。

useMemo 的應用

useMemo 與 useCallback 很像,根據上述 useCallback 已經能夠想到 useMemo 也能針對傳入子組件的值進行緩存優化,固然這個值必須是一個對象,若是不是對象而是一些簡單類型的如字符串等,那麼沒更改 React.memo 也能對比出來,下面就直接舉個 🌰 對比一下。

// ...
const [count, setCount] = useState(0);

const userInfo = {
  // ...
  age: count,
  name: 'Jace'
}

return <UserCard userInfo={userInfo}> 複製代碼
// ...
const [count, setCount] = useState(0);

const userInfo = useMemo(() => {
  return {
    // ...
    name: "Jace",
    age: count
  };
}, [count]);

return <UserCard userInfo={userInfo}> 複製代碼

很明顯的上面的 userInfo 每次都將是一個新的對象,不管 count 發生改變沒,都會致使 UserCard 從新渲染,而下面的則會在 count 改變後纔會返回新的對象。

實際上 useMemo 的做用不止於此,根據官方文檔內介紹,它主要的功能應該是:

This optimization helps to avoid expensive calculations on every render.

能夠吧一些昂貴的計算邏輯放到 useMemo 中,只有當依賴值發生改變的時候纔去更新。

const num = useMemo(() => {
  let num = 0;
  // 這裏使用 count 針對 num 作一些很複雜的計算,當 count 沒改變的時候,組件從新渲染就會直接返回以前緩存的值。
  return num;
}, [count]);

return <div>{num}</div>
複製代碼

也能在不少狀況將兩種狀況結合起來用。

結束。

相關文章
相關標籤/搜索