为什么 useReducer 钩子不更新状态?

时间:2021-05-28 22:36:55

标签: reactjs state use-context use-reducer

我正在 React 中处理登录/注册组件,我正在使用 useContext 和 useReducer 钩子来管理状态。这是我第一次尝试这种方式,我不确定为什么状态没有改变。以下是登录组件的文件。我已经展示了控制台记录的位置以及结果是什么。

这是API:

export const login = ({ email, password }) => {

  console.log(email, password); 
  // jennifer@jennifer.com 12345678

  return fetch(`${DEV_AUTH_URL}/signin`, {
    method: 'POST',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      email,
      password,
    }),
  })
    .then((res) => {
      return res.ok
        ? res.json()
        : res.json().then(err => PromiseRejectionEvent.reject(err));
    })
    .then(data => data);
};

这是状态管理器:

const AuthState = (props) => {
  const initialState = {
    token: null,
    isAuth: false,
    errorMsg: null,
    user: {},
  };

  const [state, dispatch] = useReducer(AuthReducer, initialState);
  const history = useHistory();

  const handleLogin = (formData) => {
    login(formData)
      .then((res) => {

        console.log(res); 
        // {token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7I…zk3fQ.Qx9zDeXBecToIEScCTDXzkBiTnATHab4cnyg0aSMdLE"}


        res && res.token
          ? dispatch({ type: LOGIN_SUCCESS, payload: res })
          : dispatch({ type: LOGIN_FAIL, payload: res });
      })
      .then(() => {
        closeAllPopups();

        console.log('jwt: ', localStorage.getItem('jwt'));
        // {token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7I…zk3fQ.Qx9zDeXBecToIEScCTDXzkBiTnATHab4cnyg0aSMdLE"}

        console.log('token: ', state.token);
        // token:  null
        console.log('isAuth: ', state.isAuth);
        // isAuth: false

        history.push('/');
      })
      .catch((err) => dispatch({ type: LOGIN_FAIL, payload: err.toString() }));
  };

这是减速器中的内容:

import {
  LOGIN_SUCCESS,
  LOGIN_FAIL,
} from '../types';

export default (state, action) => {
  switch (action.type) {
    case LOGIN_SUCCESS:
      localStorage.setItem('jwt', action.payload.token);
      return {
        ...state,
        token: action.payload.token, // this is not changing state
        isAuth: true, // this is also not changing state
      };
    case LOGIN_FAIL:
      return {
        ...state,
        token: null,
        user: {},
        isAuth: false,
        errorMsg: action.payload,
      };
    default:
      return state;
  }
};

1 个答案:

答案 0 :(得分:0)

问题

有点不清楚您真正想要的逻辑流是什么,但 React 状态更新是异步的,并且 handleLogin 回调被调用的渲染周期中的状态在其生命周期内包含在回调范围内功能。仅仅因为 React 状态更新是异步的并不意味着它们都可以等待。

解决方案

据我所知,您想调用 login,登录成功后调度一个操作,关闭弹出窗口,记录一些值,然后导航回家。除了记录更新的状态外,这一切都可以在第一个 thenable 中完成。

const handleLogin = (formData) => {
  login(formData)
    .then((res) => {
      console.log(res); 

      res?.token
        ? dispatch({ type: LOGIN_SUCCESS, payload: res })
        : dispatch({ type: LOGIN_FAIL, payload: res });

      closeAllPopups();
      history.push('/');
    })
    .catch((err) => {
      dispatch({ type: LOGIN_FAIL, payload: err.toString() });
    });
};

使用 useEffect 钩子记录任何状态更新。

useEffect(() => {
  console.log('jwt: ', localStorage.getItem('jwt'));
  console.log('token: ', state.token);
  console.log('isAuth: ', state.isAuth);
}, [state]);