这是一个代码沙箱。
getIDs()
更新cells
,然后initializeCells()
需要。但是,调度操作后不会反映此更改。尽管如此,我可以在 Redux 开发工具上看到操作通过,cells
的值也相应发生了变化。gameStart()
通过 props 传递给子组件cells
,并通过useEffect()
钩子调用那里。我需要传递一个空数组作为这个钩子的第二个参数,否则它将永远运行,因为每次调用它时状态都会更新。问题是新状态在首次运行时getIDs()
后不可用于以下函数。似乎是gameStart()
完全完成并再次被召唤的时候。我需要在完成后立即更新initializeCells()
getIDs()
状态。
细胞.js
import React, { useEffect } from "react";
import { useSelector } from "react-redux";
import Cell from "./Container/Container/Cell";
const Cells = props => {
const board = useSelector(state => state.board);
useEffect(() => {
props.gameStart();
}, []);
return (
<div id="cells">
{board.map(cell => {
return (
<Cell
id={cell.id.substring(1)}
key={cell.id.substring(1)}
className="cell"
/>
);
})}
</div>
);
};
export default Cells;
应用.js
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import {
setCells,
setBoard
} from "../../redux/actions/index";
const Game = () => {
const dispatch = useDispatch();
const cells = useSelector(state => state.cells);
const board = useSelector(state => state.board);
const boardSize = useSelector(state => state.boardSize);
async function gameStart() {
await getIDs();
console.log(cells); // []
await initializeCells();
await assignSnake();
await placeFood();
await paintCells();
}
function getIDs() {
let cellID = "";
let collection = [];
for (let i = 1; i <= boardSize.rows; i++) {
for (let j = 1; j <= boardSize.columns; j++) {
cellID = `#cell-${i}-${j}`;
collection.push(cellID);
}
}
dispatch(setCells(collection));
console.log(cells); // []
}
function initializeCells() {
console.log(cells); // []
const board = [];
// for loop never runs because cells is empty
for (let i = 0; i < cells.length; i++) {
board.push(cell(cells[i]));
}
dispatch(setBoard(board));
console.log("Board: ", board); // []
}
function cell(id) {
return {
id: id,
row: id.match("-(.*)-")[1],
column: id.substr(id.lastIndexOf("-") + 1),
hasFood: false,
hasSnake: false
};
}
return (
...
)
}
export default Game;
减速器/索引器.js
import {
SET_CELLS,
SET_BOARD
} from "../constants/action-types";
const initialState = {
board: [],
cells: [],
boardSize: {
rows: 25,
columns: 40
}
};
const rootReducer = (state = initialState, action) => {
switch (action.type) {
case SET_CELLS:
return Object.assign({}, state, {
cells: action.payload
});
case SET_BOARD:
return Object.assign({}, state, {
board: action.payload
});
default:
return state;
}
};
操作/索引.js
import {
SET_CELLS,
SET_BOARD
} from "../constants/action-types";
export const setCells = payload => {
return { type: SET_CELLS, payload };
};
export const setBoard = payload => {
return { type: SET_BOARD, payload };
};
常量/操作类型.js
export const SET_CELLS = "SET_CELLS";
export const SET_BOARD = "SET_BOARD";
调度某些操作后,更新的存储将仅在下次渲染时提供。对于带有钩子的函数和具有connect
HOC 的类也是如此。
您需要更改代码,而不是立即更改。我很难理解你在这里的意图,你可以从渲染它来的东西开始,然后用行动来调度和忘记的方法。它应该有效。
如果没有,请制作最少的样本(仅相关的钩子+数据呈现方式(,并描述您想要获得的内容(而不是"如何"(
我建议你在编写代码之前重新考虑这里的所有模式,并考虑是什么决定了你的决策。首先,为什么要这样设置状态?如果使用 state 是合理的,那么当您只访问Cells
组件中的board
时,为什么要创建单独的cells
并board
状态值?是否会控制任何值(如boardSize
(?也许当应用程序加载并且您不知道它们时,它们将从远程网络资源调用?如果对其中任何一个都否,则实际上没有任何充分的理由将它们存储在状态中,它们可以只是在组件外部初始化时声明的常量。如果是,则代码应适合用例。如果要使用用户控制的电路板尺寸,则应使用默认值初始化值,并在减速器内处理所有同步状态更改,而不会产生副作用。
另外,正如您所知,您使用异步函数的方式是一种反模式。使用 Redux,如果您使用的是真正的异步函数,即调用网络资源,则可以使用 thunks,并在每次需要访问dispatch
后更新的状态时在 thunk 中调用getState()
。
否则,您是否熟悉componentDidUpdate
的类组件生命周期模式?本质上,您"侦听"状态更改,并且仅在更改后调用依赖于更改状态的函数。使用钩子可以做到这一点的一种方法是useEffect
使用包含您所依赖状态的依赖项数组,这意味着只有在这些依赖项更改时才会调用它,并且您可以在useEffect
函数中进行进一步的条件检查(但永远不要将useEffect
包装在条件中!然而,当使用对象或数组作为依赖项时,这里的事情变得更加复杂,因为它使用严格的相等性检查,因此您可能需要使用 ref 并在useEffect
中比较当前和以前的值,使用类似这样的东西 usePrevious 钩子。
综上所述,在当前的用例中,您不需要执行任何操作,因为您只需同步初始化静态值。如果不控制boardSize
值,我个人甚至不会将其存储在状态中,但为了教育起见,以下是您在减速器中的做法。
首先,只需从Game
组件disptach({ type: 'INITIALIZE_BOARD' })
即可。
然后,将所有同步逻辑封装在化简器中:
const initialState = {
board: [],
boardSize: {
rows: 25,
columns: 40
}
};
const rootReducer = (state = initialState, action) => {
switch (action.type) {
case 'INITIALIZE_BOARD': {
const { rows, columns } = state.boardSize
const board = [];
for (let row = 1; row <= rows; row++) {
for (let column = 1; column <= columns; column++) {
board.push({
id: `#cell-${row}-${column}`,
row,
column,
hasFood: false,
hasSnake: false
});
}
}
return {
...state,
board
};
}
default:
return state;
}
};