Reactjs useSelector常量在分派后不更新
这是一本书Reactjs useSelector常量在分派后不更新,reactjs,redux,react-redux,Reactjs,Redux,React Redux,这是一本书 getIDs()更新单元格,然后初始化cells()需要这些单元格。但是,在分派操作后不会反映此更改。尽管如此,我仍然可以看到动作成功了,cells的值也发生了相应的变化gameStart()通过道具传递给子组件单元格,并通过useEffect()钩子调用。我需要传递一个空数组作为这个钩子的第二个参数,否则它将永远运行,因为每次调用它时状态都会更新。问题在于,在第一次运行getIDs()之后,新状态对于以下函数不可用。似乎是在gameStart()完全完成并再次被调用时。我需要在ge
getIDs()
更新单元格
,然后初始化cells()
需要这些单元格。但是,在分派操作后不会反映此更改。尽管如此,我仍然可以看到动作成功了,cells
的值也发生了相应的变化gameStart()
通过道具传递给子组件单元格,并通过useEffect()
钩子调用。我需要传递一个空数组作为这个钩子的第二个参数,否则它将永远运行,因为每次调用它时状态都会更新。问题在于,在第一次运行getIDs()
之后,新状态对于以下函数不可用。似乎是在gameStart()
完全完成并再次被调用时。我需要在getIDs()
完成后立即更新initializeCells()
需要更新的状态片段
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 (
<div id="cells">
{board.map(cell => {
return (
<Cell
id={cell.id.substring(1)}
key={cell.id.substring(1)}
className="cell"
/>
);
})}
</div>
);
};
export default Cells;
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;
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;
}
};
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 };
};
export const SET_CELLS = "SET_CELLS";
export const SET_BOARD = "SET_BOARD";
操作/index.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;
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;
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;
}
};
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 };
};
export const SET_CELLS = "SET_CELLS";
export const SET_BOARD = "SET_BOARD";
常量/动作类型.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;
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;
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;
}
};
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 };
};
export const SET_CELLS = "SET_CELLS";
export const SET_BOARD = "SET_BOARD";
在分派某些操作后,仅在下次渲染时提供更新的存储。这对于带有钩子的函数和带有connect
HOC的类也是一样的
您需要更改代码,以避免立即更改。我很难理解你在这里的意图,你可以从呈现它的来龙去脉开始,用行动来调度和忘记方法。它应该会起作用
如果没有,则进行最小样本(仅相关挂钩+数据的呈现方式)并描述您想要获取的内容(而不是“如何”)在分派某些操作后,仅在下次呈现时提供更新的存储。这对于带有钩子的函数和带有connect
HOC的类也是一样的
您需要更改代码,以避免立即更改。我很难理解你在这里的意图,你可以从呈现它的来龙去脉开始,用行动来调度和忘记方法。它应该会起作用
如果没有,则进行最少的采样(仅相关的钩子+数据如何呈现),并描述您想要获得的内容(而不是“如何”)我建议您重新考虑这里的所有模式,并在编写代码之前考虑一下是什么通知了您的决策。首先,为什么要把国家设置成这样?如果使用状态是合理的,那么当您仅在单元
组件中访问板
时,为什么要创建单独的单元
和板
状态值?是否要控制任何值,如boardSize
?当应用程序加载时,可能会从远程网络资源调用它们,而您现在还不知道它们?如果对其中任何一个都不满意,那么就没有什么好的理由将它们存储在状态中,它们可以只是在组件外部初始化时声明的常量。如果是,代码应该适合用例。如果您打算让用户控制电路板大小,您应该使用默认值初始化您的值,并处理所有同步状态更改,而不会对减速器产生任何副作用
另外,正如您所知,使用异步函数的方式是一种反模式。使用Redux,如果您使用的是真正的异步函数,即调用网络资源,则每次需要在调度后访问更新的状态时,都可以使用并在一次thunk内调用getState()
否则,您是否熟悉componentdiddupdate
的类组件生命周期模式?本质上,您“侦听”状态更改,并且只在更改后调用依赖于更改状态的函数。使用钩子可以做到这一点的一种方法是useffect
使用包含所依赖状态的依赖项数组,这意味着只有当这些依赖项发生更改时才会调用它,并且可以在useffect
函数中执行进一步的条件检查(但决不能在条件中包装useffect
)。但是,当使用对象或数组作为依赖项时,情况会变得更加复杂,因为它使用严格的相等性检查,因此您可能需要使用ref并比较useffect
中的当前值和以前的值,例如
综上所述,在您当前的用例中,您不需要执行任何操作,因为您只是同步初始化静态值。如果boardSize
值不受控制,我个人甚至不会将其存储在州内,但为了教育起见,以下是您在减速机中的操作方法
首先,从Game
组件简单地dispatch({type:'INITIALIZE_BOARD'})
然后,将所有同步逻辑封装到reducer中:
const initialState={
董事会:[],
董事会规模:{
行:25,
栏目:40
}
};
const rootReducer=(state=initialState,action)=>{
开关(动作类型){
案例“初始化电路板”:{
const{rows,columns}=state.boardSize
const board=[];
对于(让row=1;row我建议您重新考虑这里的所有模式,并在编写代码之前考虑一下是什么通知了您的决定。首先,为什么要这样设置状态?如果使用状态是合理的,那么当您仅在单元格中访问board
时,为什么要创建单独的单元格
和board
状态值代码>组件?是否有任何值(如boardSize
等)将被控制?当应用程序加载时,可能会从远程网络资源调用这些值,而您现在不知道它们?如果对其中任何一个都不知道,则实际上没有任何理由将它们存储在状态中,它们可以只是初始化时声明的常量n在组件外部。如果是,代码应适合用例。如果要使用用户控制的板大小,则应使用默认值初始化值,并处理所有同步状态更改