使用选择器常量在调度后不更新



这是一个代码沙箱。

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";

调度某些操作后,更新的存储将仅在下次渲染时提供。对于带有钩子的函数和具有connectHOC 的类也是如此。

您需要更改代码,而不是立即更改。我很难理解你在这里的意图,你可以从渲染它来的东西开始,然后用行动来调度和忘记的方法。它应该有效。

如果没有,请制作最少的样本(仅相关的钩子+数据呈现方式(,并描述您想要获得的内容(而不是"如何"(

我建议你在编写代码之前重新考虑这里的所有模式,并考虑是什么决定了你的决策。首先,为什么要这样设置状态?如果使用 state 是合理的,那么当您只访问Cells组件中的board时,为什么要创建单独的cellsboard状态值?是否会控制任何值(如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;
}
};

最新更新