简体   繁体   中英

How to dispatch async action in Redux using Redux Thunk: TypeError: Cannot read property 'loading' of undefined

I have set everything in Redux side and I see every action in Redux Devtool. It works all perfect. The problem occurs when I want to dispatch action in React Component. In login component I want to dispatch action, wait its response then depending on response redirect it to a page or show errors. Here are my codes:

userActions.js

import axios from "axios";
import {
  LOGIN_USER,
  LOGIN_USER_SUCCESS,
  LOGIN_USER_FAILED,
} from "./types";

const loginUserRequest = () => {
  return {
    type: LOGIN_USER,
  };
};

const loginUserSuccess = (user) => {
  return {
    type: LOGIN_USER_SUCCESS,
    payload: user,
  };
};

const loginUserFailed = (error) => {
  return {
    type: LOGIN_USER_FAILED,
    payload: error,
  };
};

export const loginUser = (dataSubmitted) => {
  return (dispatch) => {
    dispatch(loginUserRequest());
    axios
      .post("/api/users/login", dataSubmitted)
      .then((response) => {
        dispatch(loginUserSuccess(response.data));
      })
      .catch((err) => {
        dispatch(loginUserFailed(err));
      });
  };
};

userReducer.js:

import {
  LOGIN_USER,
  LOGIN_USER_SUCCESS,
  LOGIN_USER_FAILED,
} from "../actions/types";

const initialState = {
  loading: false,
  user: "",
  error: "",
};

export default function (state = initialState, action) {
  switch (action.type) {
    case LOGIN_USER:
      return { ...state, loading: true };

    case LOGIN_USER_SUCCESS:
      return { ...state, loading: false, user: action.payload, error: "" };

    case LOGIN_USER_FAILED:
      return { ...state, loading: false, user: "", error: action.payload };

    default:
      return state;
  }
}

The above codes works great and does the job. The problem is in following code where I am dispatching the async action. After I run the code I get this.props.userData as undefined.

Login.js

import React, { Component } from "react";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { loginUser } from "../../actions/userActions";

export class Login extends Component {
  state = {
    email: "",
    password: "",
    errors: [],
  };

  displayErrors = (errors) =>
    errors.map((error, i) => (
      <div className="alert alert-danger" key={i}>
        {error}
      </div>
    ));

  handleSubmit = (e) => {
    e.preventDefault();
    var { email, password } = this.state;
    var errors = [];

    if (email === "") {
      errors.push("Email is required");
    }

    if (password === "") {
      errors.push("Password is required");
    }

    this.setState({ errors: errors });
    //Problem occurs here
    this.props.dispatch(loginUser({ email, password }));

    if (response.payload.success) {
      sessionStorage.setItem("jwt", response.payload.token);
      sessionStorage.setItem("userId", response.payload._id);
      this.props.history.push("/");
    } else {
      errors.push("Username and/or Password is not correct");
      this.setState({ errors: errors });
    }
  };

  render() {
    return (
      <form className="form-signin" onSubmit={this.handleSubmit}>
        <h1 className="h3 mb-3 font-weight-normal">Sign in</h1>

        {this.state.errors.length > 0 && this.displayErrors(this.state.errors)}
        <label for="inputEmail" className="sr-only">
          Email address
        </label>
        <input
          type="email"
          id="inputEmail"
          className="form-control"
          placeholder="Email address"
          value={this.state.email}
          onChange={(e) => {
            this.setState({ email: e.target.value });
          }}
          required
          autoFocus
        />
        <label for="inputPassword" className="sr-only">
          Password
        </label>
        <input
          type="password"
          id="inputPassword"
          className="form-control"
          placeholder="Password"
          value={this.state.password}
          onChange={(e) => {
            this.setState({ password: e.target.value });
          }}
          required
        />
        <div className="checkbox mb-3">
          <label>
            <input type="checkbox" value="remember-me" /> Remember me
          </label>
        </div>
        <button
          className="btn btn-lg btn-primary btn-block"
          type="submit"
          onClick={this.handleSubmit}
        >
          Sign in
        </button>
        <Link to="/register">Sign Up</Link>
      </form>
    );
  }
}

function mapStateToProps(state) {
  return {
    userData: state.user,
  };
}

export default connect(mapStateToProps)(Login);

You need to install connected-react-router to manipulate with a history inside redux:

import { push } from 'connected-react-router';

export const loginUser = (dataSubmitted) => {
  return (dispatch) => {
    dispatch(loginUserRequest());
    axios
      .post("/api/users/login", dataSubmitted)
      .then((response) => {
        dispatch(loginUserSuccess(response.data));

        if (response.payload.success) {
          sessionStorage.setItem("jwt", response.payload.token);
          sessionStorage.setItem("userId", response.payload._id);
          push("/");
        } else {
          errors.push("Username and/or Password is not correct");
        }
      })
      .catch((err) => {
        dispatch(loginUserFailed(err));
      });
  };
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM