103

Redux docs for bindActionCreators states that:

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.

What would be an example where bindActionCreators would be used/needed?

Which kind of component would not be aware of Redux?

What are the advantages/disadvantages of both options?

//actionCreator
import * as actionCreators from './actionCreators'

function mapStateToProps(state) {
  return {
    posts: state.posts,
    comments: state.comments
  }
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators(actionCreators, dispatch)
}

vs

function mapStateToProps(state) {
  return {
    posts: state.posts,
    comments: state.comments
  }
}

function mapDispatchToProps(dispatch) {
  return {
    someCallback: (postId, index) => {
      dispatch({
        type: 'REMOVE_COMMENT',
        postId,
        index
      })
    }
  }
}
Tasawar Hussain
  • 650
  • 7
  • 21
d.code
  • 1,033
  • 2
  • 8
  • 4

11 Answers11

67

I don't think that the most popular answer, actually addresses the question.

All of the examples below essentially do the same thing and follow the no "pre-binding" concept.

// option 1
const mapDispatchToProps = (dispatch) => ({
  action: () => dispatch(action())
})


// option 2
const mapDispatchToProps = (dispatch) => ({
  action: bindActionCreators(action, dispatch)
})


// option 3
const mapDispatchToProps = {
  action: action
}

Option #3 is just a shorthand for option #1 , so the real question why one would use option #1 vs option #2. I've seen both of them used in react-redux codebase, and I find it is rather confusing.

I think the confusion comes from the fact that all of the examples in react-redux doc uses bindActionCreators while the doc for bindActionCreators (as quoted in the question itself) says to not use it with react-redux.

I guess the answer is consistency in the codebase, but I personally prefer explicitly wrapping actions in dispatch whenever needed.

Tasawar Hussain
  • 650
  • 7
  • 21
Diana Suvorova
  • 1,104
  • 1
  • 10
  • 10
  • 3
    how is option `#3` a shorthand for option `#1`? – ogostos Oct 15 '18 at 10:17
  • 1
    @ogostos https://github.com/reduxjs/react-redux/blob/master/docs/api.md#inject-todos-and-specific-action-creators-addtodo-and-deletetodo-with-shorthand-syntax – Artem Bernatskyi Oct 17 '18 at 21:54
  • @ArtemBernatskyi thanks. So, turns out there are 3 cases for `mapDispatchToProps`: `function`, `object` and missing. How to handle each case is defined [here](https://github.com/reduxjs/react-redux/blob/master/src/connect/mapDispatchToProps.js) – ogostos Oct 18 '18 at 11:56
  • I have been looking for this answer. Thanks. – Kishan Rajdev Jan 16 '19 at 06:32
  • I've actually come across the specific use case that the React docs talk about now *"pass some action creators down to a component that isn't aware of Redux"* because I have a simple component connected to a more complex component and adding the overhead of `redux` and `connect` and `addDispatchToProps` to the simple component seems overkill if I can just pass one prop down. But as far as I'm aware almost all cases for `mapDispatch` to props will either be options `#1` or `#3` mentioned in the answer – icc97 Mar 25 '19 at 10:53
48

99% of the time, it's used with the React-Redux connect() function, as part of the mapDispatchToProps parameter. It can be used explicitly inside the mapDispatch function you provide, or automatically if you use the object shorthand syntax and pass an object full of action creators to connect.

The idea is that by pre-binding the action creators, the component you pass to connect() technically "doesn't know" that it's connected - it just knows that it needs to run this.props.someCallback(). On the other hand, if you didn't bind action creators, and called this.props.dispatch(someActionCreator()), now the component "knows" that it's connected because it's expecting props.dispatch to exist.

I wrote some thoughts on this topic in my blog post Idiomatic Redux: Why use action creators?.

markerikson
  • 63,178
  • 10
  • 141
  • 157
  • 1
    But it's connected with 'mapDispatchToProps', which is fine. It seems binding action creators is actually a negative/pointless thing since you'll lose the function definition (TS or Flow) amongst many other things like debugging. On my newer projects I never use it and I've not had a problem to date. Also using Saga and persist state. Also, if you are just calling redux functions (you get nice click though) i'd say that's even cleaner then 'attaching' action creators which in my opinion is messy and pointless. Your smart (usually screen components) can still use connect, only for props though. – Oliver Dixon Feb 27 '19 at 11:45
20

More complete example, pass an object full of action creators to connect:

import * as ProductActions from './ProductActions';

// component part
export function Product({ name, description }) {
    return <div>
        <button onClick={this.props.addProduct}>Add a product</button>
    </div>
}

// container part
function mapStateToProps(state) {
    return {...state};
}

function mapDispatchToProps(dispatch) {
    return bindActionCreators({
        ...ProductActions,
    }, dispatch);
}

export default connect(mapStateToProps, mapDispatchToProps)(Product);
Charlie 木匠
  • 2,234
  • 19
  • 19
16

I'll try to answer the original questions ...

Smart & Dumb components

In your first question you basically ask why bindActionCreators is needed in the first place, and what kind of components should not be aware of Redux.

In short the idea here is that components should be split into smart (container) and dumb (presentational) components. Dumb components work on the need-to-know basis. Their soul job is to render given data to HTML and nothing more. They shouldn't be aware of inner workings of the application. They can be seen as the skin deep front layer of your application.

On the other hand smart components are kind of a glue, which prepares data for the dumb components and preferably does no HTML rendering.

This kind of architecture promotes loose coupling between UI layer and the data layer underneath. This in turns allows easy replacement of any of the two layers with something else (i.e. a new design of the UI), which will not break the other layer.

To answer your question: dumb components shouldn't be aware of Redux (or any unnecessary implementation detail of the data-layer for that matter) because we might want to replace it with something else in the future.

You can find more about this concept in the Redux manual and in greater depth in article Presentational and Container Components by Dan Abramov.

Which example is better

The second question was about advantages/disadvantages of the given examples.

In the first example the action creators are defined in a separate actionCreators file/module, which means they can be reused elsewhere. It's pretty much the standard way of defining actions. I don't really see any disadvantages in this.

The second example defines action creators inline, which has multiple disadvantages:

  • action creators can't be re-used (obviously)
  • the thing is more verbose, which translates to less readable
  • action types are hard coded - it's preferably to define them as consts separately, so that they can be referenced in reducers - that would reduce a chance for typing mistakes
  • defining action creators inline is against recommended/expected way of using them - which will make your code a bit less readable for the community, in case you plan to share your code

The second example has one advantage over the first one - it's faster to write! So if you don't have greater plans for your code, it might be just fine.

I hope I managed to clarify things a bit ...

knee-cola
  • 736
  • 8
  • 17
2

One possible use of bindActionCreators() is to "map" multiple actions together as a single prop.

A normal dispatch looks like this:

Map a couple common user actions to props.

const mapStateToProps = (state: IAppState) => {
  return {
    // map state here
  }
}
const mapDispatchToProps = (dispatch: Dispatch) => {
  return {
    userLogin: () => {
      dispatch(login());
    },
    userEditEmail: () => {
      dispatch(editEmail());
    },
  };
};
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent);

In larger projects mapping each dispatch separately can feel unwieldy. If we have a bunch of actions that are related to each other we can combine these actions. For example a user action file that did all kinds of different user related actions. Instead of calling each action as a separate dispatch we can use bindActionCreators() instead of dispatch.

Multiple Dispatches using bindActionCreators()

Import all your related actions. They are likely all in the same file in the redux store

import * as allUserActions from "./store/actions/user";

And now instead of using dispatch use bindActionCreators()

    const mapDispatchToProps = (dispatch: Dispatch) => {
      return {
           ...bindActionCreators(allUserActions, dispatch);
        },
      };
    };
    export default connect(mapStateToProps, mapDispatchToProps, 
    (stateProps, dispatchProps, ownProps) => {
      return {
        ...stateProps,
        userAction: dispatchProps
        ownProps,
      }
    })(MyComponent);

Now I can use the prop userAction to call all the actions in your component.

IE: userAction.login() userAction.editEmail() or this.props.userAction.login() this.props.userAction.editEmail().

NOTE: You do not have to map the bindActionCreators() to a single prop. (The additional => {return {}} that maps to userAction). You can also use bindActionCreators() to map all the actions of a single file as separate props. But I find doing that can be confusing. I prefer having each action or "action group" be given an explicit name. I also like to name the ownProps to be more descriptive about what these "child props" are or where they are coming from. When using Redux + React it can get a bit confusing where all the props are being supplied so the more descriptive the better.

Matt
  • 1,388
  • 15
  • 16
2

by using bindActionCreators, it can group multiple action functions and pass it down to a component that does not aware of Redux (Dumb Component) like so

// actions.js

export const increment = () => ({
    type: 'INCREMENT'
})

export const decrement = () => ({
    type: 'DECREMENT'
})
// main.js
import { Component } from 'react'
import { bindActionCreators } from 'redux'
import * as Actions from './actions.js'
import Counter from './counter.js'

class Main extends Component {

  constructor(props) {
    super(props);
    const { dispatch } = props;
    this.boundActionCreators = bindActionCreators(Actions, dispatch)
  }

  render() {
    return (
      <Counter {...this.boundActionCreators} />
    )
  }
}
// counter.js
import { Component } from 'react'

export default Counter extends Component {
  render() {
    <div>
     <button onclick={() => this.props.increment()}
     <button onclick={() => this.props.decrement()}
    </div>
  }
}
JXLai
  • 312
  • 4
  • 10
2

The only use case for binding action creator is when you want to pass some action creators down to a component that is not aware of redux. So it helps me create useActions hook:

import { useDispatch } from "react-redux";
import { bindActionCreators } from "redux";
// I exported all the actions as actionCreators
// export * as actionCreators from "./action-creators";
import { actionCreators } from "../state";

export const useActions = () => {
  const dispatch = useDispatch();
  return bindActionCreators(actionCreators, dispatch);
};

actionCreators are action creator functions that I exported all from a file. For example lets say I have updatePost action creator

export const updatePost = (id: string, content: string): UpdatePostAction => {
  return { type: ActionType.UPDATE_POST, payload: { id, content } };
};

So whenever I need to dispatch updatePost action, I write this:

const {updatePost}=useActions()
updatePost({id,content})
Yilmaz
  • 35,338
  • 10
  • 157
  • 202
1

I was also looking for to know more about bindActionsCreators and here is how i implemented in my project.

// Actions.js
// Action Creator
const loginRequest = (username, password) => {
 return {
   type: 'LOGIN_REQUEST',
   username,
   password,
  }
}

const logoutRequest = () => {
 return {
   type: 'LOGOUT_REQUEST'
  }
}

export default { loginRequest, logoutRequest };

In your React Component

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import ActionCreators from './actions'

class App extends Component {
  componentDidMount() {
   // now you can access your action creators from props.
    this.props.loginRequest('username', 'password');
  }

  render() {
    return null;
  }
}

const mapStateToProps = () => null;

const mapDispatchToProps = dispatch => ({ ...bindActionCreators(ActionCreators, dispatch) });

export default connect(
  mapStateToProps,
  mapDispatchToProps,
)(App);
Bimal Grg
  • 7,624
  • 2
  • 24
  • 21
0

One nice use case for bindActionCreators is for integration with redux-saga using redux-saga-routines. For example:

// routines.js
import { createRoutine } from "redux-saga-routines";
export const fetchPosts = createRoutine("FETCH_POSTS");
// Posts.js
import React from "react";
import { bindActionCreators } from "redux";
import { connect } from "react-redux";
import { fetchPosts } from "routines";

class Posts extends React.Component {
  componentDidMount() {
    const { fetchPosts } = this.props;
    fetchPosts();
  }

  render() {
    const { posts } = this.props;
    return (
      <ul>
        {posts.map((post, i) => (
          <li key={i}>{post}</li>
        ))}
      </ul>
    );
  }
}

const mapStateToProps = ({ posts }) => ({ posts });
const mapDispatchToProps = dispatch => ({
  ...bindActionCreators({ fetchPosts }, dispatch)
});

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Posts);
// reducers.js
import { fetchPosts } from "routines";

const initialState = [];

export const posts = (state = initialState, { type, payload }) => {
  switch (type) {
    case fetchPosts.SUCCESS:
      return payload.data;
    default:
      return state;
  }
};
// api.js
import axios from "axios";

export const JSON_OPTS = { headers: { Accept: "application/json" } };
export const GET = (url, opts) =>
  axios.get(url, opts).then(({ data, headers }) => ({ data, headers }));
// sagas.js
import { GET, JSON_OPTS } from "api";
import { fetchPosts } from "routines";
import { call, put, takeLatest } from "redux-saga/effects";

export function* fetchPostsSaga() {
  try {
    yield put(fetchPosts.request());
    const { data } = yield call(GET, "/api/posts", JSON_OPTS);
    yield put(fetchPosts.success(data));
  } catch (error) {
    if (error.response) {
      const { status, data } = error.response;
      yield put(fetchPosts.failure({ status, data }));
    } else {
      yield put(fetchPosts.failure(error.message));
    }
  } finally {
    yield put(fetchPosts.fulfill());
  }
}

export function* fetchPostsRequestSaga() {
  yield takeLatest(fetchPosts.TRIGGER, fetchPostsSaga);
}

Note that this pattern could be implemented using React Hooks (as of React 16.8).

tomc
  • 1,458
  • 1
  • 10
  • 16
0

I feel like too few people talk about when NOT to use bindActionCreators. I've seen numerous times when people create hooks with bindActionCreators just to use it in their components that are perfectly aware of redux.

But there is no point in doing this. Just use hooks. useDispatch, import actions from your slices directly in component file and you're good. You don't need bindActionCreators in these cases.

And there is nothing wrong with writing dispatch(fooAction) all the time, dispatch fn will nicely separate redux actions from other function (e.g. from props) so it makes it even easier to read things.

It is only if you have some pure UI component, that is NOT connected to redux in any way and that just expects plain functions, there, to construct "pure" functions you can use bindActionCreators and pass the results down to your dumb component.

Maksim Nesterenko
  • 5,661
  • 11
  • 57
  • 91
-1

The docs statement is very clear:

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.

This is clearly a use case that may arise in the following and only one condition:

Lets say, we have component A and B:

// A use connect and updates the redux store
const A = props => {}
export default connect()(A)

// B doesn't use connect therefore it does not know about the redux store.
const B = props => {}
export default B

Inject to react-redux: (A)

const boundActionCreators = bindActionCreators(SomeActionCreator, dispatch)
// myActionCreatorMethod,
// myActionCreatorMethod2,
// myActionCreatorMethod3,

// when we want to dispatch
const action = SomeActionCreator.myActionCreatorMethod('My updates')
dispatch(action)

Injected by react-redux: (B)

const { myActionCreatorMethod } = props
<B myActionCreatorMethod={myActionCreatorMethod} {...boundActionCreators} />

Noticed of the following?

  • We updated the redux store through the component A whilst we were unknown of redux store in component B.

  • We're not updating in component A. To know what exactly I mean, you may explore this post. I hope you'll have an idea.

Karl Taylor
  • 4,839
  • 3
  • 34
  • 62
Bhojendra Rauniyar
  • 83,432
  • 35
  • 168
  • 231