reactjs - 从 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”,
解决方案
减速器不应该是异步的并且有任何副作用,例如进行 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])
推荐阅读
- css - CSS 在我的 WordPress 博客主页上不起作用
- django - 在不使用 Relay 的情况下对石墨烯查询使用 django_filters
- rust - 实现读取特征:特征绑定`std::error::Error + 'static: std::marker::Sized`不满足
- redirect - Symfony 4 使用语言环境
- apache-flink - Apache Flink 1.3.2 与 Kafka 1.1.0 的连接问题
- c++ - PCL 读取 PLY 错误
- java - Apache PdfBox,如何检查页面上的可用空白空间
- c# - Oracle.ManagedDataAccess.dll 中发生了“OracleInternal.NotificationServices.ONSException”类型的第一次机会异常
- magento2 - 如何在结帐页面中将应用优惠券移动到 Magento 2 的侧栏?
- android - 如何从片段验证 recyclerview 适配器 TextInputEditText?