使用React-Hooks,如果其中一个兄弟姐妹改变状态,如何防止从Array.map创建的组件重新渲染?

问题描述 投票:1回答:1

我使用array.map创建了一个组件网格。使用console.log我能够看到每个组件正在改变状态时每个组件都会重新渲染。当我有一个50x50的网格时,这变得非常慢。

import React, { useState } from 'react';

function Cell({ cell, cellState, updateBoard }) {

  console.log('cell rendered')

  const CellStyle = {
    display: 'inline-block',
    width: '10px',
    height: '10px',
    border: '1px green solid',
    background: cellState ? 'green' : 'purple'
  };

  function handleClick(e) {
    updateBoard(cell, !cellState)
  }

  return (
    <span
      style={CellStyle}
      onClick={handleClick}
    />
  )
}

function App() {

  console.log('board rendered')

  const initialState = new Array(10).fill().map(() => new Array(10).fill(false));

  let [board, setBoard] = useState(initialState);

  function updateBoard(cell, nextState) {
    let tempBoard = [...board];
    tempBoard[cell[0]][cell[1]] = nextState;
    setBoard(tempBoard)
  }

  return (
    <div style={{ display: 'inline-block' }}>
      {board.map((v, i, a) => {
        return (
          <div
            key={`Row${i}`}
            style={{ height: '12px' }}
          >
            {v.map((w, j) =>
              <Cell
                key={`${i}-${j}`}
                cell={[i, j]}
                cellState={board[i][j]}
                updateBoard={updateBoard}
              />
            )}
          </div>
        )
      }
      )}
    </div>
  )
}

export default App;

当我单击其中一个组件时,我希望更新父状态,并更新和重新呈现所单击的组件。由于其余组件未更改,我不希望其他组件重新呈现。我如何使用React-Hook实现这一目标?

javascript reactjs react-hooks
1个回答
2
投票

几乎没有什么能够大大提高性能:

  1. 使用memo()
const MemoizedCell = memo(Cell);
/*...*/
<MemoizedCell 
  /*...*/
/>
  1. 每次都没有将新的引用传递给<Cell />

你正在传递cell={[i, j]} - 它每次调用它时都会创建新的数组(!),这意味着Cell的道具已被更改 - 为什么它不再渲染呢?

与传递updateBoard={updateBoard}相同 - 每次<App />呈现时,您都在创建新功能。你需要记住它并在功能中使用旧状态。

  const updateBoard = useCallback(
    (cell, nextState) => {
      setBoard(oldBoard => {
        let tempBoard = [...oldBoard];
        tempBoard[cell[0]][cell[1]] = nextState;
        return tempBoard;
      });
    },
    [setBoard]
  );
  1. 你在每个渲染中创建initialState - 将它移动到<App />上方(外部)或在useState中创建它作为函数(并在此使用const而不是let)。
const [board, setBoard] = useState(() =>
  new Array(10).fill().map(() => new Array(10).fill(false))
);

最终解决方案:

import React, { useState, memo, useCallback } from "react";
import ReactDOM from "react-dom";

function Cell({ i, j, cellState, updateBoard }) {
  console.log(`cell ${i}, ${j} rendered`);

  const CellStyle = {
    display: "inline-block",
    width: "10px",
    height: "10px",
    border: "1px green solid",
    background: cellState ? "green" : "purple"
  };

  function handleClick(e) {
    updateBoard([i, j], !cellState);
  }

  return <span style={CellStyle} onClick={handleClick} />;
}

const MemoizedCell = memo(Cell);

function App() {
  console.log("board rendered");

  const [board, setBoard] = useState(() =>
    new Array(10).fill().map(() => new Array(10).fill(false))
  );

  const updateBoard = useCallback(
    (cell, nextState) => {
      setBoard(oldBoard => {
        let tempBoard = [...oldBoard];
        tempBoard[cell[0]][cell[1]] = nextState;
        return tempBoard;
      });
    },
    [setBoard]
  );

  return (
    <div style={{ display: "inline-block" }}>
      {board.map((v, i, a) => {
        return (
          <div key={`Row${i}`} style={{ height: "12px" }}>
            {v.map((w, j) => (
              <MemoizedCell
                key={`${i}-${j}`}
                i={i}
                j={j}
                cellState={board[i][j]}
                updateBoard={updateBoard}
              />
            ))}
          </div>
        );
      })}
    </div>
  );
}

export default App;
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
© www.soinside.com 2019 - 2024. All rights reserved.