我使用 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-Hooks 完成此操作?
事情可以大大提高性能:
- 使用
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);