为什么useSelector返回初始状态,而在redux开发工具中显示更新状态

ocebsuys  于 2023-05-18  发布在  其他
关注(0)|答案(1)|浏览(129)

我正在使用redux,不知道为什么我在尝试访问时会获得初始状态,但它在redux开发工具中显示更新状态。
我在做什么:我有两个按钮**“Yes”“No”,在reducer中,我有初始状态,所以当我点击“yes”按钮时,我使用调度方法更新初始状态,我能够在redux dev工具中看到更新的状态,但是当我在“No”**按钮的点击功能中从redux访问状态时,它显示初始状态:

reducer.js

import { SET_MODAL_DETAILS, CLEAR_MODAL_DETAILS } from "../actions/types";

const initialState = {
  modalInfo: {
    isOpen: false,
    modalName: "",
    title: "",
    content: "",          
  },
};

const modal = (state = initialState, action) => {
  const { type, payload } = action;

  switch (type) {
    case SET_MODAL_DETAILS:
      return { ...state, modalInfo: payload };

    case CLEAR_MODAL_DETAILS:
      return { ...state, modalInfo: null };

    default:
      return state;
  }
};
export default modal;

action.js

import {SET_MODAL_DETAILS, CLEAR_MODAL_DETAILS} from "./types";

export const setModalDetails = (payload) => ({
    type: SET_MODAL_DETAILS,
    payload: payload,
});

export const clearModalDetails = () => ({
    type: CLEAR_MODAL_DETAILS,
});

store.js

import { createStore, applyMiddleware } from "redux";
import { composeWithDevTools } from "redux-devtools-extension";
import thunk from "redux-thunk";
import rootReducer from "./reducers";
import checkTokenExpiration from "./checkToken";

const middleware = [thunk];

const store = createStore(
  rootReducer,
  process.env.NODE_ENV === "production"
    ? applyMiddleware(...middleware, checkTokenExpiration)
    : composeWithDevTools(applyMiddleware(...middleware, checkTokenExpiration))
);

export default store;

student.js

import { connect } from "react-redux";
import Button from "@mui/material/Button";
import { setModalDetails } from "../../redux/actions/modal";

const Student = ({ setModalDetails, modal: { modalInfo } }) => {
  const handleYesButton = () => {
    setModalDetails({
      ...modalInfo,
      isOpen: true,
      title: "Confirm Modal",
      content: "Do you want to change status?",
      modalName: "confirmModal",
    });
  };

  const handleNoButton = () => {
    console.log(modalInfo); // returns initial state while I have updated via above funtion
  };

  return (
    <>
      <Button onClick={handleYesButton}>Yes</Button>
      <Button onClick={handleNoButton}>No</Button>
    </>
  );
};

const mapStateToProps = (state) => ({
  modal: state.modal,
});

export default connect(mapStateToProps, { setModalDetails })(Student);
70gysomp

70gysomp1#

如果我从父组件传递这两个函数,那么它就不会像预期的那样工作,但是如果我在子组件中使用这两个函数,比如在student.js中,那么它就可以工作。你能说说原因吗?
这一切都在Student(student.js)中工作,因为Student直接订阅了存储中的state.modal状态。当存储更新时,Student将重新呈现,并将最新选择的modal状态值作为prop注入,modal.modalInfo将重新包含在handleNoButton函数范围中。当单击“no”按钮时,它具有当前的modalInfo值。
StudentA(studentA.js)中按钮单击处理程序 * 不 * 工作的情况下,这是因为您关闭了state.modal.modalInfo的某个本地状态的陈旧副本,该副本从未更新。

const Parent = ({ setModalDetails, modal: { modalInfo } }) => {
  const handleYesButton = () => {
    setModalDetails({
      ...modalInfo,
      isOpen: true,
      title: "Confirm Modal",
      content: "Do you want to change status?",
      modalName: "confirmModal"
    });
  };

  const handleNoButton = () => {
    console.log("from parent: ", modalInfo);
  };

  const [
    confirmOptions,
    setConfirmOptions // <-- never called to update confirmOptions
  ] = useState({
    buttons: [
      {
        label: "Yes",
        onClick: handleYesButton // <-- store state closed over!
      },
      {
        label: "No",
        onClick: handleNoButton  // <-- store state closed over!
      }
    ]
  });

  return <StudentA options={confirmOptions} />;
};

当redux状态被更新并且Parent重新呈现时,setConfirmOptions不会被调用来更新confirmOptions的值,因此StudentA被传递给当前的confirmOptions状态,该状态现在包含陈旧的redux状态值。
如果你想创建一个传递的选项对象,那么只需要直接计算并传递它。这样,在Parent渲染的任何时候,都会使用当前选定的状态值创建新的confirmOptions对象。
示例:

const confirmOptions = {
  buttons: [
    {
      label: "Yes",
      onClick: handleYesButton
    },
    {
      label: "No",
      onClick: handleNoButton
    }
  ]
};

return <StudentA options={confirmOptions} />;

如果这样做会触发更多的StudentA渲染,或者创建对象的计算量很大,那么应该使用useMemo钩子来记忆并提供一个稳定的选项对象。但是,请注意,handleYesButtonhandleNoButton在创建confirmOptions对象时是外部依赖项,因此它们也需要被存储以提供稳定的引用。
示例:

const handleYesButton = useCallback(() => {
  setModalDetails({
    ...modalInfo,
    isOpen: true,
    title: "Confirm Modal",
    content: "Do you want to change status?",
    modalName: "confirmModal"
  });
}, [setModalDetails, modalInfo]);

const handleNoButton = useCallback(() => {
  console.log("from parent: ", modalInfo);
}, [modalInfo]);

const confirmOptions = useMemo(
  () => ({
    buttons: [
      {
        label: "Yes",
        onClick: handleYesButton
      },
      {
        label: "No",
        onClick: handleNoButton
      }
    ]
  }),
  [handleNoButton, handleYesButton]
);

return <StudentA options={confirmOptions} />;

或者因为handleYesButtonhandleNoButtonParent中的其他地方没有被引用,所以它们可以被移动到useMemo钩子回调中。

const confirmOptions = useMemo(
  () => ({
    buttons: [
      {
        label: "Yes",
        onClick: () => {
          setModalDetails({
            ...modalInfo,
            isOpen: true,
            title: "Confirm Modal",
            content: "Do you want to change status?",
            modalName: "confirmModal"
          });
        }
      },
      {
        label: "No",
        onClick: () => {
          console.log("from parent: ", modalInfo);
        }
      }
    ]
  }),
  [modalInfo, setModalDetails]
);

return <StudentA options={confirmOptions} />;

相关问题