useSelector常量在分派后不更新

时间:2019-12-26 13:21:15

标签: reactjs redux react-redux

这里是Codesandbox

getIDs()更新cellsinitializeCells()然后需要。但是,此更改在分派操作后不会反映出来。尽管如此,我仍然可以在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 (
    <div id="cells">
      {board.map(cell => {
        return (
          <Cell
            id={cell.id.substring(1)}
            key={cell.id.substring(1)}
            className="cell"
          />
        );
      })}
    </div>
  );
};

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

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({}, state, {
        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";

2 个答案:

答案 0 :(得分:1)

我建议您在这里重新考虑所有的模式,并在编写代码之前考虑一下可以通知您的决定的内容。首先,为什么要这样设置状态?如果使用状态是合理的,那么当您仅在cells组件中访问board时,为什么要创建单独的boardCells状态值?是否会控制诸如boardSize之类的任何值?也许在应用加载时会从远程网络资源中调用它们,而您不立即知道它们吗?如果这两个都不对,则没有任何充分的理由将它们存储在状态中,它们可以只是在组件外部初始化时声明的常量。如果是,则代码应适合用例。如果要由用户控制电路板的尺寸,则应使用默认值初始化值,并处理所有同步状态更改,而在reducer内没有副作用。

此外,正如您所知,使用异步函数的方式有点像反模式。使用Redux,如果您使用的是真正的异步功能,即调用网络资源,则可以使用thunks,并在每次{{ 1}}。

否则,您是否熟悉getState()的类组件生命周期模式?本质上,您“监听”状态更改,并且仅在状态更改后调用依赖于状态更改的函数。您可以使用钩子执行此操作的一种方法是dispatch使用包含您所依赖状态的依赖项数组,这意味着只有在这些依赖项发生更改时才会调用它,并且您可以在{中进行进一步的条件检查{1}}函数(但从不有条件地包装componentDidUpdate!)。当使用对象或数组作为依赖项时,事情变得更加复杂,因为它使用严格的相等性检查,因此您可能需要使用ref并比较useEffect中的当前值和先前值,以及类似{{3} }。

所有这些,在您当前的用例中,您不需要执行任何操作,因为您只是在同步初始化静态值。如果useEffect值不受控制,我个人甚至不会将其存储在状态中,但是出于教育的目的,这是您在化简器中的操作方式。

首先,只需使用useEffect组件中的useEffect

然后,将所有同步逻辑封装在减速器中:

boardSize

答案 1 :(得分:0)

在分派一些动作后,仅在下一次渲染时才会提供更新的存储。对于具有钩子和带有_m HOC的类的函数,这是相同的。

您需要更改代码,以免立即发生更改。对于我来说,很难理解您的意图,您可能首先要渲染它的来龙去脉,并采取行动来分而忘却。而且应该可以。

如果不这样做,请进行最少的采样(仅使用相关的钩子+数据呈现方式),并描述您要获取的内容(而不是“如何”)