Redux docs for bindActionCreators states that:
The only use case for
bindActionCreatorsis when you want to pass some action cre
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);
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).
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.
The docs statement is very clear:
The only use case for
bindActionCreatorsis 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.