iT邦幫忙

2024 iThome 鐵人賽

DAY 17
0
Modern Web

雙向奔赴的websocket與冰冷的react系列 第 17

[day17]React優化(1)useMemo 和 useCallback

  • 分享至 

  • xImage
  •  

接下來要說的是關於React的一些優化方法

useMemo 和 useCallback 是 React 中的兩個優化 Hook,它們的作用是避免不必要的計算和函數重新創建,從而提升性能。

useMemo:避免不必要的重新計算

useMemo 用於記憶一個計算結果,只有當依賴項改變時,才會重新進行計算。這在有複雜計算或需要優化性能的場景中非常有用。

未使用(參照物)

  • 明顯的區別在於未使用的你在input輸入會卡卡的,有使用則不會。
import React, { useState } from 'react';

function App() {
    const [count, setCount] = useState(0);
    const [input, setInput] = useState('');

    // 這是一個耗時的計算函數
    const expensiveCalculation = (num) => {
        console.log('進行了計算');
        for (let i = 0; i < 1000000000; i++) {} // 模擬耗時計算
        return num * 2;
    };

    // 每次渲染都會重新計算
    const result = expensiveCalculation(count);

    return (
        <div>
            <h1>Expensive Calculation Result: {result}</h1>
            <button onClick={() => setCount(count + 1)}>增加計數: {count}</button>
            <input value={input} onChange={(e) => setInput(e.target.value)} />
        </div>
    );
}

export default App;

在這個範例中,每次輸入框的值改變時,整個組件都會重新渲染,並且 expensiveCalculation 都會重新計算,這導致不必要的性能浪費。

  • 原因
    在 React 中,每次狀態變更(無論是 count 還是 input),整個 App 函數組件都會重新執行一次,這也就意味著所有的代碼(包括 expensiveCalculation(count))都會重新運行。
  • 具體來說
    當你在輸入框中輸入時,setInput 會觸發狀態變更,React 會重新渲染 App 組件。這就導致 App 函數整體重新運行。即使 count 沒有改變,因為你每次渲染都在調用它。

解決方案:使用 useMemo

為了避免這種不必要的計算,我們可以使用 useMemo。useMemo 會記憶某次計算的結果,只有當依賴項(這裡是 count)改變時,才會重新執行計算。

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

function App() {
    const [count, setCount] = useState(0);
    const [input, setInput] = useState('');

    const expensiveCalculation = (num) => {
        console.log('進行了計算');
        for (let i = 0; i < 1000000000; i++) {} // 模擬耗時計算
        return num * 2;
    };

    // 只有當 count 改變時才重新計算
    const result = useMemo(() => expensiveCalculation(count), [count]);

    return (
        <div>
            <h1>Expensive Calculation Result: {result}</h1>
            <button onClick={() => setCount(count + 1)}>增加計數: {count}</button>
            <input value={input} onChange={(e) => setInput(e.target.value)} />
        </div>
    );
}

export default App;

在這個優化範例中,我們使用 useMemo 來記憶 expensiveCalculation 的結果。只有當 count 改變時,useMemo 才會重新執行計算,這樣可以避免在輸入框每次輸入時重新進行不必要的計算。

useCallback:避免不必要的函數重新創建

useCallback 用來記憶一個回調函數,當依賴項不變時,它會返回同一個函數實例。這在將回調函數傳遞給子組件時非常有用,避免每次父組件渲染時,子組件重新渲染。

未使用(參照物)

import React, { useState } from 'react';

const Button = ({ onClick }) => {
    console.log('Button 組件重新渲染'); // 用來檢查 Button 是否重新渲染
    return <button onClick={onClick}>點擊我</button>;
};

function App() {
    const [count, setCount] = useState(0);
    const handleClick = () => {
        console.log('按鈕被點擊');
    };

    return (
        <div>
            <h1>計數: {count}</h1>
            <Button onClick={handleClick} />
            <button onClick={() => setCount(count + 1)}>增加計數</button>
        </div>
    );
}

export default App;

在這個範例中,每次 App 組件重新渲染時,handleClick 函數都會重新創建,導致子組件 Button 每次也重新渲染。

原因就跟上面一樣由狀態變更導致的重覆觸發。

解決方案:使用 useCallback

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

// 使用 React.memo 優化 Button 組件
const Button = React.memo(({ handleClick }) => {
    console.log('Button 組件重新渲染');
    return <button onClick={handleClick}>點擊我</button>;
});

function App() {
    const [count, setCount] = useState(0);
    // 使用 useCallback 記憶 handleClick 函數
    const handleClick = useCallback(() => {
        console.log('按鈕點擊');
    }, []); // 這裡的依賴是空數組,表示 handleClick 永遠不會變化

    return (
        <div>
            <h1>計數: {count}</h1>
            <Button handleClick={handleClick} />
            <button onClick={() => setCount(count + 1)}>增加計數</button>
        </div>
    );
}

export default App;

使用 useCallback,我們將 handleClick 函數記憶起來,這樣當 App 組件重新渲染時,Button 組件不會因為 handleClick 的改變而重新渲染。

然後裡面有熟悉的[]依賴向,如果在其中填入count他就會被重新渲染了,可以很有效的控管哪些項目觸發時需要被渲染,哪些不需要被渲染

今天就到這了,謝謝


上一篇
[day16]React基礎教學(6)自定義hook與組件
下一篇
[day18]React優化(2)Component vs memo
系列文
雙向奔赴的websocket與冰冷的react30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言