Redux DevTools Time Travel не может корректно обновить пользовательский интерфейс (только при отмене действий)

Я нахожусь на ранней стадии разработки игры React+Redux и следую рекомендациям Redux: чистый редуктор, разделение презентаций и контейнерных компонентов, используя getState() только в Reducer (в отличие от создателя действий) и т. д. Приложение, кажется, работает должным образом, но когда я пытаюсь отменить действие, используя Time Travel, даже если свойство состояния map[][] и он рассчитывает изменение реквизита подключенного компонента, как и ожидалось, результат не отражается должным образом в пользовательском интерфейсе (в частности, положение игрока на карте не соответствует тому, что диктует состояние). Когда я проверяю изменения состояния, я вижу, что все необходимые изменения правильно происходят между различными состояниями. Вот мой редуктор:

const gridReducer = (state, action) => {

  if (typeof state === 'undefined'){
    let dungeon = new Dungeon();
    dungeon.generate();
    return {
      boardWidth: Math.floor(((70/100) * window.innerWidth) / 20),
      boardHeight: Math.floor(((70/100) * window.innerHeight) / 20),
      map: dungeon.map,
      position: dungeon.playerPosition
    }
  }
  switch (action.type) {
    case 'GRID_RESIZE': 
      return {...state, 
              boardWidth: action.newBoardWidth,
              boardHeight: action.newBoardHeight
      }
    //This is where I have the issue, map correctly changes both when interacting with the game and when reversing using time travel however the UI fails to update (only in reverse)!
    case 'MOVE':
      let dungeonObj = new Dungeon(state.map.slice(), {...state.position});
      if (dungeonObj.movePlayer(action.direction)) {
        return {...state,
                position: dungeonObj.playerPosition,
                map: dungeonObj.map
               }
      } else return state;
    default:
      return state;
  }
}

Вот полный код, если вы хотите посмотреть! В настоящее время приложение поддерживает перемещение игрока в подземелье только путем нажатия клавиш со стрелками, и предполагается, что представление всегда будет центральным в зависимости от положения игрока (игрок не может вернуться назад при использовании перемещения во времени) http://s.codepen.io/sabahang/debug/GjrPNQ

PS: Dungeon.generate использует Math.Random, но я использую эту функцию только в initialState а для отправленных действий я делаю только поверхностную копию сгенерированной карты, отправляя текущее состояние конструктору Dungeon и используя другие его методы (например, movePlayer)

1 ответ

Решение

Нашли виновника. Это вовсе не вина Redux, а то, как работает React! Если вы новичок в React и еще не попали в эту ловушку, подождите! Это связано с тем, что большинство традиционных способов копирования глубоко вложенных объектов, которые необходимы в Redux для реализации чистого Reducer, фактически делают поверхностную копию ссылок на память объектов и свойств, все еще указывающих на оригинал. Государственный. React обновляет пользовательский интерфейс на основе глубокого сравнения старого состояния и нового, и когда некоторые ссылки совпадают, он не может правильно обновить пользовательский интерфейс. Здесь у меня есть двумерный массив map[][] который является объектом, и хотя я использую оператор распространения ES6, чтобы избежать изменения исходного состояния, потому что создается теневая копия, глубоко вложенные индексы оригинала map[][] в настоящее время изменяются. Одним из решений было бы использование Array.map() для создания совершенно нового объекта, но в итоге я использовал immutablejs, и это исправило мою проблему с помощью ползунка перемещения во времени.

Это очень рекомендуемый справочник, если вы не хотите проводить недели, преследуя подобные ошибки в сложных приложениях: http://redux.js.org/docs/recipes/reducers/ImmutableUpdatePatterns.html

и есть множество помощников по неизменяемости, которые помогут вам в зависимости от ваших конкретных потребностей: https://github.com/markerikson/redux-ecosystem-links/blob/master/immutable-data.md#immutable-update-utilities

Это также выглядит интересно только для Redux: https://github.com/indexiatech/redux-immutablejs

Этот вопрос может быть дубликатом следующего: React-redux store обновляет, но React не делает

Другие вопросы по тегам