首页 > 解决方案 > 从 reducer 分派异步调用后无法获取更新状态

问题描述

在向异步调用发送操作后,我正在尝试获取用户的更新状态并将其存储在用户的上下文中。我确实在 reducer 函数中获取了数据,但是返回到我的组件的状态没有改变。

我将异步调用放在我的 reducer 函数中,并在 App.js 中调用 useReducer。之后,我将状态和从 App.js 分派给它可能需要的任何组件。

我可能做错了什么,因为我还是 React 的新手。这是我的尝试:

// UserContext.js
import React from "react";

// create user context
const UserContext = React.createContext({
  status: "",
  message: "",
  username: "",
  password: "",
  users: []
});

export default UserContext;
// UserReducer.js
import axios from "axios";

// import action
import { AUTH_USER } from "./UserActionTypes";

// import endpoint
import { AUTH } from "../../constants";

// authenticate user
const authenticateUser = async (payload, state) => {
  // API call
  const response = await axios.post(`${AUTH}?type=${payload.type}`, {
    username: payload.username,
    password: payload.password
  });

  const data = response.data;

  console.log(`API Data: ${JSON.stringify(data)}`); // data shown

  return {
    ...state,
    status: data.status,
    message: data.message,
    users: data.results
  };
};

const UserReducer = async (state, action) => {
  switch (action.type) {
    case AUTH_USER:
      const newState = await authenticateUser(action.payload, state);
      console.log(`New State: ${JSON.stringify(newState)}`); // new state shown
      return newState;
    default:
      return state;
  }
};

export default UserReducer;
// App.js
import UserContext from "./context/User/UserContext";
import UserReducer from "./context/User/UserReducer";

export default function App() {
  // initial state
  const initialState = {
    status: "",
    message: "",
    username: "",
    password: "",
    users: []
  };

  // useReducer
  const [state, dispatch] = useReducer(UserReducer, initialState);

  return (
    <UserContext.Provider value={{ state, dispatch }}>
      <Router history={history}>
        <Routes />
      </Router>
    </UserContext.Provider>
  );
}
// Login.js
import React, { useState, useContext } from "react";
import UserContext from "../../context/User/UserContext";
import { AUTH_USER } from "../../context/User/UserActionTypes";

const Login = () => {
  const { state, dispatch } = useContext(UserContext);

  // initail form data
  let initialForm = {
    username: "",
    password: "",
    usernameError: null,
    passwordError: null
  };

  // useState
  const [form, setForm] = useState(initialForm);

  // submit form
  const handleSubmit = e => {
    e.preventDefault();

    if (!form.username) {
      setForm({ ...form, usernameError: "الرجاء إدخال اسم المستخدم" });
      return;
    }

    if (!form.password) {
      setForm({ ...form, passwordError: "الرجاء إدخال كلمة المرور" });
      return;
    }

    // dispatch auth action
    dispatch({
      type: AUTH_USER,
      payload: {
        username: form.username,
        password: form.password,
        type: "db"
      }
    });

    console.log(`Context State: ${JSON.stringify(state)}`); // here (state) is still empty
  };

  return (
    <DirectionProvider direction={DIRECTIONS.RTL}>
      <div className="centered">
        <form onSubmit={e => handleSubmit(e)}>
          <Pane>
            <TextInputField
              name="username"
              placeholder="اسم المستخدم"
              value={form.username || ""}
              onChange={e => {
                setForm({ ...form, username: e.target.value });
              }}
              validationMessage={form.usernameError}
            />
            <TextInputField
              type="password"
              name="password"
              placeholder="كلمة المرور"
              value={form.password || ""}
              onChange={e => {
                setForm({ ...form, password: e.target.value });
              }}
              validationMessage={form.passwordError}
            />
            <Button type="submit">
              تسجيل الدخول
            </Button>
          </Pane>
        </form>
      </div>
    </DirectionProvider>
  );
};

export default Login;

我正在使用:react:“^16.12.0”,react-router:“^5.1.2”,react-router-dom:“^5.1.2”,

标签: reactjsreact-hooksreact-context

解决方案


减速器不应该是异步的并且有任何副作用,例如进行 api 调用。他们所做的只是将更改应用于状态以产生新状态。它们应该是纯函数。

因此,在您的情况下,您需要在 reducer 之外对用户进行身份验证,并将该调用的结果传递给 reducer 以计算新状态。您很可能需要调度更新状态的操作,这些操作authenticateUser需要移动到Login组件中:

// submit form
  const handleSubmit = async e => {
    e.preventDefault();

    if (!form.username) {
      setForm({ ...form, usernameError: "الرجاء إدخال اسم المستخدم" });
      return;
    }

    if (!form.password) {
      setForm({ ...form, passwordError: "الرجاء إدخال كلمة المرور" });
      return;
    }

    const newState = await authenticateUser({
        username: form.username,
        password: form.password,
        type: "db"
      });

    // dispatch auth action
    dispatch({
      type: AUTH_USER,
      payload: newState
    });
  };
const UserReducer = (state, action) => {
  switch (action.type) {
    case AUTH_USER:
      return {...state, ...action.payload};
    default:
      return state;
  }
};

还请记住,您在这里分派异步操作,因此如果您在分派后立即 console.log 状态,您仍然会看到状态的旧值。要检查新值,您可以使用useEffect钩子,或将 console.log 移出handleSubmit

useEffect(() => {
  console.log(state) // get updated value of the state
}, [state])

推荐阅读