Accessing props from state using mergeProps
While using Redux, you may come across a situation where you are passing in props from both mapStateToProps and mapDispatchToProps, and using them together:
// Button.js
const Button = ({ name, setName }) => (
<button onClick={setName(name)}>Click</button>
);
const mapStateToProps = (state) => ({
name: getName(state),
});
const mapDispatchToProps = (dispatch) => ({
setName: (name) => dispatch(setName(name)),
});
export default connect(mapStateToProps, mapDispatchToProps)(Button);
We can save Button having to know about name, and instead use mergeProps:
// Button.js
const Button = ({ setName }) => (
<button onClick={setName}>Click</button>
);
const mapStateToProps = (state) => ({
name: getName(state),
});
const mapDispatchToProps = (dispatch) => ({
setName: (name) => () => dispatch(setName(name))
});
const mergeProps = (stateProps, dispatchProps) => ({
setName: dispatchProps.setName(stateProps.name),
});
export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(Button);
What does mergeProps do?
mergeProps is an optional third argument you can pass into connect. As the name suggests, it merges all the props into one object for your component to use. By default, it will look like this:
(stateProps, dispatchProps, ownProps) =>
({ ...stateProps, ...dispatchProps, ...ownProps })
-
statePropsare all the props frommapStateToProps- in the above example,name -
dispatchPropsare all the props frommapDispatchToProps-setName -
ownPropsare all props that are passed into a component like this<Button foo={bar}/>
Accessing ownProps in mapDispatchFromProps
We can also access ownProps from mapDispatchToProps. Here we have the same Button example, but instead of name coming from mapStateToProps, this time it’s being passed in from the Form component:
// Form.js
import Button from './Button';
const Form = () => (
<>
{/* A bunch of other stuff... */}
<Button name={'Emma'} />
</>
);
// Button.js
const Button = ({ name, setName }) => (
<button onClick={setName(name)}>Click</button>
);
const mapDispatchToProps = (dispatch) => ({
setName: (name) => dispatch(setName(name)),
});
export default connect(null, mapDispatchToProps)(Button);
We can use the name prop directly in mapDispatchToProps by using its second argument, ownProps:
const Button = ({ setName }) => (
<button onClick={setName}>Click</button>
);
const mapDispatchToProps = (dispatch, ownProps) => ({
setName: () => dispatch(setName(ownProps.name)),
});
export default connect(null, mapDispatchToProps)(Button);
Even if name is now unused, it will still be passed in as part of ownProps to the Button component. We can filter it out using mergeProps:
const mergeProps = (stateProps, dispatchProps, ownProps) => ({
...dispatchProps,
});
export default connect(null, mapDispatchToProps, mergeProps)(Button);
Pro-tip: Using mapDispatchToProps’ object form
You’ll notice that I always defined mapDispatchToProps in its function form:
const mapDispatchToProps = (dispatch) => ({
setName: (name) => dispatch(setName(name))
});
If you’re not making use of ownProps or mergeProps, we can actually simplify it down to its object form, which does the exact same thing:
const mapDispatchToProps = {
setName,
};
Thanks for reading!
Top comments (0)