0

I´m trying to build my first connection between a React component to Redux to gather data from my node API.

This is a simple component for now, but it will grow in the future and will derive subcomponents that will allow me to build a full User CRUD interface (list, edit, delete, view, etc.). In that sense, I need to connect the action functions to the object this.props so that it can be inheritated by the child components.

Here is my code:

Component UserGrid - A table data grid that will load users information

import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as userActions from '../../actions/userActions';

class UserGrid extends React.Component {
  componentWillMount() {
    this.props.fetchUsers();
  }

  render() {
    const mappedUsers = users.map(user => <li>{user.email}</li>);
    return (
            <div>
              <ul>{mappedUsers}</ul>
            </div>
        );
    }
}

function mapStateToProps(state) {
    return {
        users: state.users
    }
}

export default connect(
    mapStateToProps, 
    bindActionCreators(userActions, dispatch)
)(UserGrid);

My userAction, that effectively will load users from an AJAX call:

import axios from "axios";

export function fetchUsers() {
  return function(dispatch) {
    axios.get("/api/users")
      .then((response) => {
        dispatch({type: "FETCH_USERS_FULFILLED", payload: response.data});
      })
      .catch((err) => {
        dispatch({type: "FETCH_USERS_REJECTED", payload: err});
      })
  }
}

With this code I´m getting the following error:

Uncaught ReferenceError: dispatch is not defined
    at Object.<anonymous> (bundle.js:37984)
    at __webpack_require__ (bundle.js:556)
    at fn (bundle.js:87)
    at Object.<anonymous> (bundle.js:37711)
    at __webpack_require__ (bundle.js:556)
    at fn (bundle.js:87)
    at Object.<anonymous> (bundle.js:8466)
    at __webpack_require__ (bundle.js:556)
    at fn (bundle.js:87)
    at Object.<anonymous> (bundle.js:588)
(anonymous) @ bundle.js:37984
__webpack_require__ @ bundle.js:556
fn @ bundle.js:87
(anonymous) @ bundle.js:37711
__webpack_require__ @ bundle.js:556
fn @ bundle.js:87
(anonymous) @ bundle.js:8466
__webpack_require__ @ bundle.js:556
fn @ bundle.js:87
(anonymous) @ bundle.js:588
__webpack_require__ @ bundle.js:556
(anonymous) @ bundle.js:579
(anonymous) @ bundle.js:582

What is the correct way to connect my component to the Redux services created?

1
  • You are passing dispatch as an argument to bindActionCreators but it isn't defined anywhere. Commented Feb 13, 2017 at 16:55

3 Answers 3

1

In the component you wish to connect you need to define the function mapDispatchToProps. This function will take dispatch as a parameter, and will receive this parameter when connect calls the function.

Here is some example code pulled from one of my recent projects.

   function mapDispatchToProps(dispatch) {
        return {
            adminActions: bindActionCreators(adminOrdersActions, dispatch),
            schoolActions: bindActionCreators(schoolOrdersListActions, dispatch),
            userActions: bindActionCreators(userActions, dispatch)
        };
    }

export default connect(mapStateToProps, mapDispatchToProps)(AllOrders);

Now dispatch is defined.

EDIT: To clarify, doing this will now give you access to your actions with the following syntax. this.props.actions.yourAction, or whatever you called the actions in the mapDispatchToProps.

This is your code with my changes:

import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';

import * as userActions from '../../actions/userActions';

class UserGrid extends React.Component {

  componentWillMount() {
    console.log(JSON.stringify(this.props));

    //this.props.fetchUsers();
    this.props.actions.fetchUsers();
  }

  render() {

    const mappedUsers = users.map(user => <li>{user.email}</li>)

    return (
            <div>
            <ul>{mappedUsers}</ul>
            </div>
        )
    }
}

function mapStateToProps(state) {
    return {
        users: state.users
    }
}

function mapDispatchToProps(dispatch) {
    // this function will now give you access to all your useractions by simply calling this.props.actions.
    //if the key of this object would not be actions, but rather foobar, you will get your actions by 
    // calling this.props.foobar
    return {
        actions: bindActionCreators(userActions, dispatch)
    }
}

export default connect(
    mapStateToProps, 
    mapDispatchToProps
)(UserGrid);
Sign up to request clarification or add additional context in comments.

7 Comments

Doing that I got the following error: bindActionCreators expected an object or a function, instead received undefined. Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?
import * as is the way to go import * as schoolOrdersListActions from '../actions/schoolOrdersListActions';
Now another error: Uncaught TypeError: this.props.fetchUsers is not a function at UserGrid.componentWillMount (bundle.js:37951)
this.props.actions.yourfunctioname
Uncaught TypeError: Cannot read property 'fetchUsers' of undefined with this.props.actions.fetchUsers. The actions are not being loaded.
|
1

I don't think you want to use bindActionCreators here. Here's what the docs say (emphasis mine):

Normally you should just call dispatch directly on your Store instance. If you use Redux with React, react-redux will provide you with the dispatch function so you can call it directly, too.

The only use case for bindActionCreators is when you want to pass some action creators down to a component that isn't aware of Redux, and you don't want to pass dispatch or the Redux store to it.

Since that's not what you're doing, you should instead call dispatch directly. With react-redux, connect will inject it into your props, so you can just do this:

componentWillMount() {
  this.props.dispatch(fetchUsers());
}

2 Comments

Yes, I´m aware, but I want to use this.props.fetchUsers() instead of this.props.dispatch(fetchUsers()). UserGrid is a now simple component but will grow and have subcomponents that will need to use the dispatch functions directly on their props.
Your question doesn't really express that. Could you edit it to more clearly demonstrate your use case?
1

As Jordan said I would not use bindActionCreators.

Your component should look something like this:

import React  from 'react';
import { connect } from 'react-redux';

import myAction from './my-action';

class MyComponent extends React.Component {

    componentDidMount() {
        this.props.myAction();
    }

    render() {
        return "Hi!"
    }

}

const mapStateToProps = (state) => ({
    myState: state.myState
});

const mapDispatchToProps = (dispatch) => ({
    myAction: () => dispatch(myAction())
});

export default connect(mapStateToProps, mapDispatchToProps)(MyComponent);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.