Производительность при работе с большими матрицами в React - PullRequest
0 голосов
/ 02 апреля 2020

У меня есть игра Minesweeper, написанная на React-Redux с несколькими c визуальными эффектами.

Игра должна поддерживать очень большие доски в виде матриц (до 400x400).

Чтобы избежать изменчивости, я должен перерисовывать всю матрицу каждый раз, когда игрок нажимает на плитку.

Для больших досок (70x70 +) это занимает некоторое время, а для очень больших досок - практически неиграбельно ( первоначальный рендеринг также время от времени).

Я рассматривал React window / React virtuazlied, но обычно вся доска (или почти вся доска) помещается на экране, поэтому в окне ничего нет. Я также пытался использовать React memo, но, похоже, это не имеет большого значения.

Есть ли что-нибудь, что сделает игру пригодной для игры с большими досками?

Вот как выглядит так: enter image description here

Вот так выглядит часть рендеринга:

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>
  )
}
}

1 Ответ

0 голосов
/ 04 апреля 2020

Используйте Redux! Вы переопределяете КАЖДУЮ плитку, если что-то в этом массиве изменяется. Соедините каждую плитку с избыточным хранилищем и получите ваши значения по идентификатору или индексу, чтобы уменьшить повторное отображение В конце должен отображаться только измененный компонент.

 <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]))} // do this in the component and not pass the value
          superman={props.superman}
          makePlay={handlePlay}
          setFlag={handleFlag}
          endGame={endGame}
        />

Вместо этого, только передайте

 <Tile x={i} y={j} key={`${i},${j}`}
          superman={props.superman}
          makePlay={handlePlay}
          setFlag={handleFlag}
          endGame={endGame}
        />

, чем вы должны подключить свою функцию к хранилищу избыточности, как

const Tile = ({ x, y, value, isFlagged, isRevealed, superman, makePlay, setFlag, endGame, gameState }) => {
  const [ currentValue, setValue ] = useState(gameState.board[x][y])
  // some fancy stuff

  useEffect((
    // do something
  ) => {}, [ gameState.board[x][y] ])


  return (<div>{currentValue}<div/>)
}

const mapStateToProps = (state) {
  return {
    gamestate: state.yourStore.gameState,
  }
}

connect(mapStateToProps, null)(Tile)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...