Search code examples
reduxreact-reduxredux-thunk

Actions must be plain objects. Use custom middleware for async actions. after adding redux-thunk and using it as a middleware


Actions must be plain objects. Use custom middleware for async actions.

This is the error and this function is pointed

this.props.registerUser(newUser);

see the next code snippet!

redux-thunk is used for referring plain js object but not working

This is my action creator file :

import { GET_ERRORS } from "./types";
import axios from "axios";

// Register User
export const registerUser = userData => dispatch => {
axios
.post("/api/users/register", userData)
.then(res => console.log(res))
.catch(err =>
  dispatch({
    type: GET_ERRORS,
    payload: err.response.data
  })
 );
};

This is my react registration page :

import React, { Component } from "react";
import PropTypes from "prop-types";
// import { withRouter } from 'react-router-dom';
import classnames from "classnames";
import { connect } from "react-redux";
import { registerUser } from "../../actions/authActions";

class Register extends Component {
  constructor() {
    super();
    this.state = {
      name: "",
      email: "",
      password: "",
      password2: "",
      errors: {}
    };

    this.onChange = this.onChange.bind(this);
    this.onSubmit = this.onSubmit.bind(this);
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.errors) {
      this.setState({ errors: nextProps.errors });
    }
  }

  onChange(event) {
    this.setState({ [event.target.name]: event.target.value });
  }
  onSubmit(event) {
    event.preventDefault();
    const newUser = {
      name: this.state.name,
      email: this.state.email,
      password: this.state.password,
      confirm_password: this.state.password2
    };
    this.props.registerUser(newUser);
  }
  render() {
    const { errors } = this.state;

    return (
      <div className="register">
        <div className="container">
          <div className="row">
            <div className="col-md-8 m-auto">
              <h1 className="display-4 text-center">Sign Up</h1>
              <p className="lead text-center">
                Create your SocioStalker account
              </p>
              <form noValidate onSubmit={this.onSubmit}>
                <div className="form-group">
                  <input
                    type="text"
                    className={classnames("form-control form-control-lg", {
                      "is-invalid": errors.name
                    })}
                    placeholder="Name"
                    name="name"
                    value={this.state.name}
                    onChange={this.onChange}
                  />
                  <div className="invalid-feedback">{errors.name}</div>
                </div>
                <div className="form-group">
                  <input
                    type="email"
                    className={classnames("form-control form-control-lg", {
                      "is-invalid": errors.email
                    })}
                    placeholder="Email Address"
                    name="email"
                    value={this.state.email}
                    onChange={this.onChange}
                  />
                  <div className="invalid-feedback">{errors.email}</div>

                  <small className="form-text text-muted">
                    This site uses Gravatar so if you want a profile image, use
                    a Gravatar email
                  </small>
                </div>
                <div className="form-group">
                  <input
                    type="password"
                    className={classnames("form-control form-control-lg", {
                      "is-invalid": errors.password
                    })}
                    placeholder="Password"
                    name="password"
                    value={this.state.password}
                    onChange={this.onChange}
                  />
                  <div className="invalid-feedback">{errors.password}</div>
                </div>
                <div className="form-group">
                  <input
                    type="password"
                    className={classnames("form-control form-control-lg", {
                      "is-invalid": errors.confirm_password
                    })}
                    placeholder="Confirm Password"
                    name="password2"
                    value={this.state.password2}
                    onChange={this.onChange}
                  />
                  <div className="invalid-feedback">
                    {errors.confirm_password}
                  </div>
                </div>
                <input type="submit" className="btn btn-info btn-block mt-4" />
              </form>
            </div>
          </div>
        </div>
      </div>
    );
  }
}

Register.propTypes = {
  registerUser: PropTypes.func.isRequired,
  auth: PropTypes.object.isRequired,
  errors: PropTypes.object.isRequired
};

const mapStateToProps = state => ({
  auth: state.auth,
  errors: state.errors
});

export default connect(
  mapStateToProps,
  { registerUser }
)(Register);

Store config file:

import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import rootReducer from "./reducers";
import { composeWithDevTools } from "redux-devtools-extension";

const initialState = {};

const middleware = [thunk];
const store = createStore(
  rootReducer,
  initialState,
  compose(
    composeWithDevTools(),
    applyMiddleware(...middleware)
  )
);

export default store;

Solution

  • Your store setup logic is wrong. You've got this:

    compose(
        composeWithDevTools(),
        applyMiddleware(...middleware)
    )
    

    Instead, it should be:

    composeWithDevTools(
        applyMiddleware(...middleware)
    )
    

    Please see the Redux docs page on "Configuring Your Store" for examples of how to correctly set up middleware and the DevTools Extension.

    I'd also encourage you to try out our new redux-starter-kit package. It includes a configureStore function that does all that for you automatically.

    Here's how you could simplify your startup file using configureStore:

    import {configureStore} from "redux-starter-kit";
    import rootReducer from "./reducers";
    
    const store = configureStore({
        reducer : rootReducer,
    });