【问题标题】:Why is useReducer hook not updating state?为什么 useReducer 钩子不更新状态?
【发布时间】:2021-08-17 03:06:55
【问题描述】:

我正在研究 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;
  }
};

【问题讨论】:

    标签: reactjs state use-context use-reducer


    【解决方案1】:

    问题

    有点不清楚您真正想要的逻辑流是什么,但是 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]);
    

    【讨论】:

    • 我在 useEffect 中记录状态更新后发现,实际上,reducer 正确更新了它们,但它们又更新了两次,并且最后一次设置回到初始状态使成为。所以现在我更好地知道如何排除故障。我不完全理解异步代码及其执行的顺序,所以它总是有点反复试验(因此是 console.logs 的数量)。谢谢!
    • 原来history.push 导致了错误,而LOGIN_FAIL 操作将一切恢复到初始状态。我已经使用了您关于在useEffect 中检查状态的建议来调试另一个组件。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-30
    • 1970-01-01
    • 2021-01-17
    • 2021-02-12
    • 2022-01-05
    • 1970-01-01
    相关资源
    最近更新 更多