分派来自reducer的异步调用后无法获取更新状态

时间:2019-12-04 07:00:27

标签: reactjs react-hooks react-context

在将操作分派给异步调用之后,我试图获取用户的更新状态并将其存储在用户上下文中。我确实在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;

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

1 个答案:

答案 0 :(得分:1)

Reducer不应异步并且有任何副作用,例如进行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])