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