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 не делает