Используя React-Hooks, как я могу предотвратить повторный рендеринг компонента, созданного из Array.map, если один из его братьев и сестер изменяет состояние? - PullRequest
1 голос
/ 11 апреля 2019

Я создал сетку компонентов, используя array.map.Используя console.log, я вижу, что каждый компонент выполняет рендеринг всякий раз, когда один из компонентов изменяет состояние.Когда у меня сетка 50х50, это становится заметно медленным.

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-Hooks?

1 Ответ

2 голосов
/ 12 апреля 2019

Есть несколько вещей, которые могут значительно улучшить производительность:

  1. с использованием memo()
const MemoizedCell = memo(Cell);
/*...*/
<MemoizedCell 
  /*...*/
/>
не передает новые ссылки на <Cell /> каждый раз

Вы передаете cell={[i, j]} - он создает новый массив каждый раз, когда вы вызываете его (!) , что означает, чтоизменен реквизит Ячеек - почему бы тогда не рендериться снова?

То же самое с передачей updateBoard={updateBoard} - вы создаете новую функцию каждый раз, когда <App /> рендерится.Вам нужно запомнить его и использовать старое состояние в функции.

  const updateBoard = useCallback(
    (cell, nextState) => {
      setBoard(oldBoard => {
        let tempBoard = [...oldBoard];
        tempBoard[cell[0]][cell[1]] = nextState;
        return tempBoard;
      });
    },
    [setBoard]
  );
вы создаете 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);
...