【问题标题】:Context & Reducer not returning State上下文和减速器不返回状态
【发布时间】:2022-01-17 09:30:37
【问题描述】:

有人可以告诉我为什么没有从减速器更新状态吗?当从减速器返回状态时,useEffect(()=>{}) 不会被触发。我已经验证了正确的信息被传递给返回,但是从 LoginScreen 中看不到任何东西。

上下文脚本


import React, { createContext, useReducer } from "react";
import userReducer from "./UserReducer";

export const UserContext = createContext();

const initialState = {
  userData: [],
  isLoggedIn: false,
  isAdmin: false,
  isEmployee: false,
  errorMessage: [{ success: false, statusCode: 0, error: null }],
};

const UserContextProvider = ({ children }) => {
  const [state, dispatch] = useReducer(userReducer, initialState);

  const registerUser = (user) =>
    dispatch({ type: "REGISTER_USER", payload: user });

  const loginUser = (user) => dispatch({ type: "LOGIN_USER", payload: user });

  const deleteUser = (user) => dispatch({ type: "DELETE_USER", payload: user });

  const updateUser = (user) => dispatch({ type: "UPDATE_USER", payload: user });

  const contextValues = {
    ...state,
    registerUser,
    loginUser,
    deleteUser,
    updateUser,
  };

  return (
    <UserContext.Provider value={contextValues}>
      {children}
    </UserContext.Provider>
  );
};

export default UserContextProvider;

减速器脚本

import axios from "axios";
axios.defaults.withCredentials = true;

const userReducer = (state = {}, action) => {
  let config = {
    header: {
      "Content-Type": "application/json",
    },
  };
  switch (action.type) {
    case "REGISTER_USER":
      break;

    case "LOGIN_USER":
      console.log(state);

      const email = action.payload.email;
      const password = action.payload.password;

      axios
        .post("/api/user/login", { email, password }, config)
        .then((response) => {
          if (response.data.success) {
            // localStorage.setItem("authToken", response.data.authToken);
            state.userData = response.data.user;
            state.isLoggedIn = true;
            if (response.data.user.role === 9) {
              state.isAdmin = true;
              state.isEmployee = true;
            } else {
              state.isAdmin = false;
              state.isEmployee = false;
            }
          }
        })
        .catch((error) => {
          state.errorMessage = {
            success: error.response.data.success,
            statusCode: error.response.status,
            message: error.response.data.error,
          };
        });

      return {
        ...state,
        userData: [state.userData],
        isLoggedIn: state.isLoggedIn,
        isAdmin: state.isAdmin,
        isEmployee: state.isEmployee,
        errorMessage: [state.errorMessage],
      };

    default:
      return state;
  }
};

export default userReducer;

登录表格

import { useState, useEffect, useContext } from "react";
import { Link } from "react-router-dom";
import {
  Button,
  Form,
  Grid,
  Message,
  Segment,
  Image,
  Container,
} from "semantic-ui-react";

//Custom Imports
import "./LoginScreen.css";
import Logo from "../../../img/logo.png";

//Context
import { UserContext } from "../../context/UserContext";

const LoginScreen = ({ history }) => {
  const { userData, loginUser, isLoggedIn, errorMessage, clearErrorMessage } =
    useContext(UserContext);
  const [user, setUser] = useState({ email: "", password: "" });
  const [error, setError] = useState("");

  useEffect(() => {
    console.log(errorMessage);
    if (localStorage.getItem("authToken")) {
      history.push("/dashboard");
    }
  }, [history]);

  useEffect(() => {
    if (isLoggedIn) {
      console.log(userData);
      console.log("User is Logged in");
      // history.push("/");
    }

    if (!errorMessage.success && errorMessage.error != null) {
      console.log(errorMessage);
      setError(errorMessage.message);
      setTimeout(() => {
        setError("");
      }, 5000);
    }
  }, [userData, errorMessage, isLoggedIn]);

  return (
    <Container className="login-container">
      <Grid
        textAlign="center"
        style={{ height: "100vh" }}
        verticalAlign="middle"
      >
        <Grid.Column style={{ maxWidth: 450 }}>
          <Image src={Logo} className="login-logo" />
          <Form size="large" onSubmit={() => loginUser(user)}>
            <Segment stacked>
              <Form.Input
                fluid
                icon="user"
                iconPosition="left"
                placeholder="Email Address"
                value={user.email}
                onChange={(e) => setUser({ ...user, email: e.target.value })}
              />
              <Form.Input
                fluid
                icon="lock"
                iconPosition="left"
                placeholder="Password"
                value={user.password}
                type="password"
                onChange={(e) => setUser({ ...user, password: e.target.value })}
              />
              {error && <span>{error}</span>}
              <Button color="blue" fluid size="large" type="submit">
                Login
              </Button>
            </Segment>
          </Form>
          <Message>
            Don't have an account? <Link to="/register">Sign Up</Link>
          </Message>
        </Grid.Column>
      </Grid>
    </Container>
  );
};

export default LoginScreen;

【问题讨论】:

  • 你不应该在你的 reducer 中产生副作用,也不应该改变状态。这很可能是不当行为的根本原因。在 useEffect 中进行 fetching,然后用 payload 派发一个 action,并用更新的数据返回新的 state

标签: reactjs react-context use-reducer


【解决方案1】:

像这样重构你的登录功能

const loginUser({ email, password }) => {
  let config = {
    header: {
      "Content-Type": "application/json",
    },
  };

  axios
        .post("/api/user/login", { email, password }, config)
        .then((response) => {
          if (response.data.success) {
            dispatch({ type: 'LOGIN_SUCCESS', payload: response.data });
          }
        })
        .catch((error) => {
          dispatch({ type: 'LOGIN_FAILED', payload: error });
        });
}

然后是你的减速器


...

switch(action.type) {
  ...
  case 'LOGIN_SUCCESS':
    // return here a new object
    // do not mutate the state (state.something = something) is not allowed

  ...

  case 'LOGIN_FAILED':
    // handle error
}

【讨论】:

    【解决方案2】:

    必备减速器概念

    Redux 和 useReducer 使用 reducer(previousState, action) =&gt; newState 一样。

    reducer 应该是 this document 中的“纯”函数。承诺、api 调用不应该在 reducer 中使用。

    问题:

    因为你在 reducer 内部调用了 api/promise。 reducer 函数在 promise 完成之前返回值。所以当 Promise 完成时,什么都不会发生。

    // A will be return before B, C are going to call
    case "LOGIN_USER":
      promiseFn()
        .then(/* B */ ...)
        .catch(/* C */ ...)
    
      // A
      return {
        ...
      }
    

    解决办法:

    the non-pure calls 与reducer 分开。并将它们放在其他代码块中(如内部挂钩、事件处理程序......)。

    【讨论】:

      猜你喜欢
      • 2019-07-05
      • 2016-11-24
      • 1970-01-01
      • 2019-11-02
      • 2021-07-18
      • 2017-09-14
      • 2018-11-20
      • 1970-01-01
      相关资源
      最近更新 更多