作者:路过然后错过 | 来源:互联网 | 2024-11-03 21:07
在使用`useSelector`选择器时,发现分派操作后状态未能实时更新。这可能是由于React组件的渲染机制或Redux的状态管理问题导致的。建议检查`useSelector`的依赖项和`dispatch`的调用时机,确保状态变化能够正确触发组件重新渲染。此外,可以考虑使用`useEffect`钩子来监听状态变化,以确保及时更新。
这里是Codesandbox。
getIDs()
更新cells
,initializeCells()
然后需要。但是,此更改在分派操作后不会反映出来。尽管如此,我仍然可以在Redux dev tools上看到该操作已通过,并且cells
的值已相应更改。 gameStart()
通过props传递到子组件cells
,并通过useEffect()
钩子在那里调用。我需要传递一个空数组作为该钩子的第二个参数,否则它将永远运行,因为每次调用时状态都会更新。问题在于,getIDs()
首次运行后,新状态不适用于以下功能。 gameStart()
似乎已经完全完成并再次被调用。我需要声明initializeCells()
完成后立即更新getIDs()
的状态。
cells.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 (
{board.map(cell => {
return (
id={cell.id.substring(1)} key={cell.id.substring(1)} classname="cell" /> ); })} |
);
};
export default Cells;
app.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 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;
reducers / index.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({},{
board: action.payload
});
default:
return state;
}
};
actions / index.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 };
};
constants / action-types.js
export const SET_CELLS = "SET_CELLS";
export const SET_BOARD = "SET_BOARD";
我建议您在这里重新考虑所有的模式,并在编写代码之前考虑一下可以通知您的决定的内容。首先,为什么要这样设置状态?如果使用状态是合理的,那么当您仅在cells
组件中访问board
时,为什么要创建单独的board
和Cells
状态值?是否会控制诸如boardSize
之类的任何值?也许在应用加载时会从远程网络资源中调用它们,而您不立即知道它们吗?如果这两个都不对,则没有任何充分的理由将它们存储在状态中,它们可以只是在组件外部初始化时声明的常量。如果是,则代码应适合用例。如果要由用户控制电路板的尺寸,则应使用默认值初始化值,并处理所有同步状态更改,而在reducer内没有副作用。
此外,正如您所知,使用异步函数的方式有点像反模式。使用Redux,如果您使用的是真正的异步功能,即调用网络资源,则可以使用thunks,并在每次{{ 1}}。
否则,您是否熟悉getState()
的类组件生命周期模式?本质上,您“监听”状态更改,并且仅在状态更改后调用依赖于状态更改的函数。您可以使用钩子执行此操作的一种方法是dispatch
使用包含您所依赖状态的依赖项数组,这意味着只有在这些依赖项发生更改时才会调用它,并且您可以在{中进行进一步的条件检查{1}}函数(但从不有条件地包装componentDidUpdate
!)。当使用对象或数组作为依赖项时,事情变得更加复杂,因为它使用严格的相等性检查,因此您可能需要使用ref并比较useEffect
中的当前值和先前值,以及类似{{3} }。
所有这些,在您当前的用例中,您不需要执行任何操作,因为您只是在同步初始化静态值。如果useEffect
值不受控制,我个人甚至不会将其存储在状态中,但是出于教育的目的,这是您在化简器中的操作方式。
首先,只需使用useEffect
组件中的useEffect
。
然后,将所有同步逻辑封装在减速器中:
boardSize
,
在分派一些动作后,仅在下一次渲染时才会提供更新的存储。对于具有钩子和带有_m
HOC的类的函数,这是相同的。
您需要更改代码,以免立即发生更改。对于我来说,很难理解您的意图,您可能首先要渲染它的来龙去脉,并采取行动来分而忘却。而且应该可以。
如果不这样做,请进行最少的采样(仅使用相关的钩子+数据呈现方式),并描述您要获取的内容(而不是“如何”)