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在组件外部。如果是,代码应适合用例。如果要使用用户控制的板大小,则应使用默认值初始化值,并处理所有同步状态更改