当我尝试登录时,Redux减速器会正确更新状态,但登录页面不会立即导航到主页。但是,如果我再次单击登录按钮,它将导航到主页。后端似乎工作正常,当我使用 Redux DevTools 检查时,它显示状态已正确更新。当我登录时,handleLogin外部的isAuthenticated会输出True,但handleLogin内的isAuthenticated会输出False。
登录组件:
import React, { useEffect } from "react";
import "./login.css";
import store from "../../store/store";
import { Form, Formik, Field } from "formik";
import { TextField, Button, CircularProgress } from "@mui/material";
import { useDispatch, useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";
import { loginUser } from "../../store/auth/auth.actions";
import * as Yup from "yup";
export default function Login() {
const navigate = useNavigate();
const dispatch = useDispatch();
const isAuthenticated = useSelector((state) => state.auth.isAuthenticated);
// console.log(isAuthenticated);
const handleLogin = async (credentials) => {
await dispatch(loginUser(credentials));
// console.log(isAuthenticated);
// const {
// auth: { isAuthenticated },
// } = store.getState();
if (isAuthenticated) {
navigate("/");
}
};
const validationSchema = Yup.object().shape({
username: Yup.string().required("Username is required"),
password: Yup.string().required("Password is required"),
});
return (
<main className="login-page">
<section className="picture-container">
<img
src={process.env.PUBLIC_URL + "/images/login.jpg"}
alt="decoration"
/>
</section>
<section className="login-section">
<section className="login-title">
<h1>Login</h1>
<h3>Log into CMUniversity</h3>
</section>
<Formik
initialValues={{ username: "", password: "" }}
validationSchema={validationSchema}
initialErrors={{ username: "" }}
onSubmit={async (values) => {
await handleLogin(values);
}}
>
{({ errors, touched, isValid }) => (
<Form className="login-form">
<Field
as={TextField}
type="text"
label="Username"
name="username"
error={touched.username && !!errors.username}
helperText={touched.username && errors.username}
required
className="form-group"
/>
<Field
as={TextField}
type="password"
label="Password"
name="password"
error={touched.password && !!errors.password}
helperText={touched.password && errors.password}
className="form-group"
required
/>
<span className="credentials">
Forgot your <span className="active">username</span> or{" "}
<span className="active">password</span>
</span>
<Button
type="submit"
variant="contained"
className="submitButton"
disabled={!isValid}
>
{/* {loading ? (<CircularProgress />):("Log in")} */}
Log In
</Button>
{/* Display error message if login failed */}
{/* {error && <p className="error-message">{error}</p>} */}
<section className="social-login">
<p>Or log in using: </p>
<Button
type="button"
variant="contained"
className="googleButton"
>
<img
src={process.env.PUBLIC_URL + "/images/google.png"}
alt="Sign in with Google"
/>
</Button>
<Button
type="button"
variant="contained"
className="facebookButton"
>
<img
src={process.env.PUBLIC_URL + "/images/facebook.png"}
alt="Sign in with Facebook"
/>
</Button>
</section>
<span className="credentials">
Not a member yet?
<span className="active">Sign Up Now</span>{" "}
</span>
</Form>
)}
</Formik>
</section>
</main>
);
}
要调度的操作:
export const loginUser = createAsyncThunk(
"auth/loginUser",
async (credentials, thunkApi) => {
try {
const { data } = await API.post("auth/login", credentials)
return data;
} catch (err) {
return thunkApi.rejectWithValue(err.message);
}
}
);
减速机:
import { createSlice } from "@reduxjs/toolkit";
import { loginUser } from "./auth.actions.js";
const authSlice = createSlice({
name: "auth",
initialState: {
user: null,
isAuthenticated: false,
error: null,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(loginUser.pending, (state, action) => {
state.user = null;
state.isAuthenticated = false;
state.error = null;
})
.addCase(loginUser.fulfilled, (state, action) => {
state.user = action.payload;
state.isAuthenticated = true;
state.error = null;
})
.addCase(loginUser.rejected, (state, action) => {
state.user = null;
state.isAuthenticated = false;
state.error = action.payload;
});
},
});
// Export reducer function by default
export default authSlice.reducer;
您的问题在这里
const handleLogin = async (credentials) => {
await dispatch(loginUser(credentials));
// console.log(isAuthenticated);
// const {
// auth: { isAuthenticated },
// } = store.getState();
if (isAuthenticated) {
navigate("/");
}
};
虽然你在
await
上使用了dispatch
,但它实际上不会等到reducer完成修改。因此,在第二次点击时,“isAuthenticated”确实已经true
,您将被导航。
正确的方法应该使用
useEffect
和 isAuthenticated
作为触发器
const handleLogin = (credentials) => {
dispatch(loginUser(credentials));
}
useEffect(()=>{ isAuthenticated && navigate("/") }, [ isAuthenticated ])
问题是
handleLogin
从调用时起就对选定的 isAuthenticated
值进行了闭包,它在回调中永远不会是不同的或更新的值。
loginUser
操作要么完成,要么被拒绝。 handleLogin
回调可以await
执行此操作。所有 Redux-Toolkit thunk 都会解析,所以关键是首先打开解析结果,看看它是被满足还是被拒绝。
有关详细信息,请参阅处理 Thunk 结果。
const handleLogin = async (credentials) => {
try {
await dispatch(loginUser(credentials)).unwrap();
// Success 😀, navigate home
navigate("/");
} catch(error) {
// Failure 🙁, handle error
// The error is the returned `thunkApi.rejectWithValue(err.message)` value
}
};