У меня есть игра Minesweeper, написанная на React-Redux с несколькими c визуальными эффектами.
Игра должна поддерживать очень большие доски в виде матриц (до 400x400).
Чтобы избежать изменчивости, я должен перерисовывать всю матрицу каждый раз, когда игрок нажимает на плитку.
Для больших досок (70x70 +) это занимает некоторое время, а для очень больших досок - практически неиграбельно ( первоначальный рендеринг также время от времени).
Я рассматривал React window / React virtuazlied, но обычно вся доска (или почти вся доска) помещается на экране, поэтому в окне ничего нет. Я также пытался использовать React memo, но, похоже, это не имеет большого значения.
Есть ли что-нибудь, что сделает игру пригодной для игры с большими досками?
Вот как выглядит так:
Вот так выглядит часть рендеринга:
const generateTable = () => {
return props.gameState.board.map((row, i) => (
row.map((column, j) => (
<Tile x={i} y={j} key={`${i},${j}`}
value={props.gameState.board[i][j]}
isFlagged={props.gameState.setFlags.has(JSON.stringify([i, j]))}
isRevealed={props.gameState.revealedTiles.has(JSON.stringify([i, j]))}
superman={props.superman}
makePlay={handlePlay}
setFlag={handleFlag}
endGame={endGame}
/>
)
)
)
)
}
return (
<section className='board' style={sectionStyle}>
{generateTable()}
</section>
)
}
Это Tile.js
:
const Tile = ({ x, y, value, isFlagged, isRevealed, superman, makePlay, setFlag, endGame }) => {
const [revealed, setRevealed] = useState(false)
const [flagged, setFlagged] = useState(false)
useEffect(() => {
setRevealed(isRevealed)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isRevealed])
useEffect(() => {
setFlagged(isFlagged)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isFlagged])
const handleLeftClick = (e) => {
if (flagged && !e.shiftKey) return
if (e.shiftKey) return setFlag([x, y], value, flagged)
setRevealed(true)
if (value === 'M') return endGame(setRevealed)
makePlay([x, y], value)
}
const displayProperValue = () => {
if (value === 'M') return <Icon fitted={true} size='small' name='fire' />
if (value === 'E') return ' '
return value
}
if (revealed) {
return (
<div className='revealed-tile' style={setStyle()}>
{displayProperValue()}
</div>
)
} else if (flagged) {
return (
<Card className='flagged-tile' raised style={hiddenStyle} onClick={handleLeftClick}>
<Icon fitted={true} name='font awesome flag' />
</Card>
)
} else if (value === 'M' && superman) {
return (
<Card className='superman-tile' raised style={hiddenStyle} onClick={handleLeftClick}>
<Icon fitted={true} name='exclamation' color='red' />
</Card>
)
} else {
return (
<Card className='hidden-tile' raised style={{...hiddenStyle, color: 'grey'}} onClick={handleLeftClick}>
{'?'}
</Card>
)
}
}