我正在尝试获取用户的更新状态,并在将操作调度到异步调用后将其存储在用户的上下文中。我确实在化简器函数中获取了数据,但返回到组件的状态没有改变。
我将异步调用放在我的化简器函数中,并在 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",
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;
}
};
另请记住,您在此处调度异步操作,因此,如果您在调度后立即控制台.log状态,您仍会看到状态的旧值。要检查新值,您可以使用useEffect
钩子,或将控制台.log移到handleSubmit
之外:
useEffect(() => {
console.log(state) // get updated value of the state
}, [state])