React Hooks 系列之6 useMemo

本系列將講述 React Hooks 的使用方法,從 useState 開始,將包含以下內容:css

  • useState
  • useEffect
  • useContext
  • useReducer
  • useCallback
  • useMemo
  • useRef
  • custom hooks

掌握 React Hooks api 將更好的幫助你在工做中使用,對 React 的掌握更上一層樓。本系列將使用大量實例代碼和效果展現,很是易於初學者和複習使用。react

上一章,咱們學習了 useCallback 來進行性能優化,關於性能優化還有另外一個 hook api,那就是 useMemo,下面咱們一塊兒經過一個例子來看看。api

計數器示例

依然是計數器示例,建立2個計數器,並能區分當前是奇數或者偶數,爲了模擬點擊按鈕時包含大量的計算邏輯影響性能,在判斷偶數的方法中添加了沒有用的計算邏輯,爲了讓性能差的明顯。代碼以下數組

Counter.tsx緩存

import React, { useState } from 'react'

function Counter() {
  const [counterOne, setCounterOne] = useState(0)
  const [counterTwo, setCounterTwo] = useState(0)

  const incrementOne = () => {
    setCounterOne(counterOne + 1)
  }

  const incrementTwo = () => {
    setCounterTwo(counterTwo + 1)
  }

  const isEven = () => {
    let i = 0
    while (i < 1000000000) i += 1
    return counterOne % 2 === 0
  }

  return (
    <div> <button onClick={incrementOne} >Count One = {counterOne}</button> <span> { isEven() ? 'even' : 'odd' } </span> <br /> <button onClick={incrementTwo} >Count Two = {counterTwo}</button> </div>
  )
}

export default Counter
複製代碼

App.tsx性能優化

import React from 'react'
import './App.css'

import Counter from './components/27.Counter'

const App = () => {
  return (
    <div className="App"> <Counter /> </div>
  )
}

export default App
複製代碼

頁面展現以下函數

咱們發現點擊第一個按鈕有較長的延遲,由於咱們的判斷偶數的邏輯中包含了大量的計算邏輯。可是,咱們點擊第二個按鈕,也有較長的延遲!這很奇怪。性能

這是由於,每次 state 更新時,組件會 rerender,isEven 會被執行,這就是咱們點擊第二個按鈕時,也會卡的緣由。咱們須要優化,告訴 React 不要有沒必要要的計算,特別是這種計算量複雜的。學習

在咱們的示例中,咱們要告訴 React,在點擊第二個按鈕時,不要執行 isEven 方法。這時就須要 useMemo hook 登場了。優化

useMemo

優化示例

與 useCallback 的用法相似。

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
複製代碼

返回一個 memoized 值。 把「建立」函數和依賴項數組做爲參數傳入 useMemo,它僅會在某個依賴項改變時才從新計算 memoized 值。這種優化有助於避免在每次渲染時都進行高開銷的計算。

記住,傳入 useMemo 的函數會在渲染期間執行。請不要在這個函數內部執行與渲染無關的操做,諸如反作用這類的操做屬於 useEffect 的適用範疇,而不是 useMemo

若是沒有提供依賴項數組,useMemo 在每次渲染時都會計算新的值。

你能夠把 useMemo 做爲性能優化的手段,但不要把它當成語義上的保證。 未來,React 可能會選擇「遺忘」之前的一些 memoized 值,並在下次渲染時從新計算它們,好比爲離屏組件釋放內存。先編寫在沒有 useMemo 的狀況下也能夠執行的代碼 —— 以後再在你的代碼中添加 useMemo,以達到優化性能的目的。

首先引入 useMemo

import React, { useState, useMemo } from 'react'
複製代碼

而後將 isEven 方法使用 useMemo 改寫,返回值賦給 isEven

const isEven = useMemo(() => {
  let i = 0
  while (i < 1000000000) i += 1
  return counterOne % 2 === 0
}, [counterOne])
複製代碼

最後記得修改 isEven 使用的地方,已經從一個方法變爲了一個變量

{
  isEven ? 'even' : 'odd'
}
複製代碼

完整代碼以下

Counter.tsx

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

function Counter() {
  const [counterOne, setCounterOne] = useState(0)
  const [counterTwo, setCounterTwo] = useState(0)

  const incrementOne = () => {
    setCounterOne(counterOne + 1)
  }

  const incrementTwo = () => {
    setCounterTwo(counterTwo + 1)
  }

  const isEven = useMemo(() => {
    let i = 0
    while (i < 1000000000) i += 1
    return counterOne % 2 === 0
  }, [counterOne])

  return (
    <div> <button onClick={incrementOne} >Count One = {counterOne}</button> <span> { isEven ? 'even' : 'odd' } </span> <br /> <button onClick={incrementTwo} >Count Two = {counterTwo}</button> </div>
  )
}

export default Counter
複製代碼

效果以下

咱們看到點擊第二個按鈕時,不會有任何卡頓,這是由於使用了 useMemo 只依賴了 counterOne 變量,點擊第二個按鈕時,isEven 讀取的是緩存值,不須要再從新計算是否爲偶數。

useMemo 與 useCallback 的區別

useCallback 是緩存了函數自身,而 useMemo 是緩存了函數的返回值。

小結

本章經過示例展現了 useMemo 在性能優化中的做用。經過緩存函數的返回值,避免沒必要要的調用,從而避免了組件 rerender。

最後有分析了 useMemo 與 useCallback 的區別,即 useMemo 是緩存了函數的返回值,useCallback 是緩存了函數自身。這兩個 api 都是性能優化的方法。

相關文章
相關標籤/搜索