成功完成Reducers后不更新状态

问题描述 投票:0回答:2

当我尝试登录时,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?&nbsp;
                <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;
reactjs ecmascript-6 react-redux redux-toolkit redux-thunk
2个回答
0
投票

您的问题在这里


 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 ])

0
投票

问题是

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
  }
};
© www.soinside.com 2019 - 2024. All rights reserved.