What's the benefit of injecting actionCreators versus a mapDispatchToProps? - javascript

I'm skimming the connect docs and trying to understand this example:
Inject todos and all action creators
import * as actionCreators from './actionCreators'
function mapStateToProps(state) {
return { todos: state.todos }
}
export default connect(mapStateToProps, actionCreators)(TodoApp)
Why is mapDispatchToProps not necessary here? Where is dispatch?
I understand actions, dispatch, and mapDispatchToProps, but I'm a little bit fuzzy on Action Creators and the above syntax.

You can pass Object or Function:
If an object is passed, each function inside it is assumed to be a Redux action creator. Connect will bind аction сreators to dispatch for you.
import * as actionCreators from './actionCreators';
connect(mapStateToProps, actionCreators)(TodoApp)
and then in component
this.props.someActionCreator();
If a function is passed, it will be given dispatch. It’s up to you to return an object that somehow uses dispatch to bind action creators in your own way. You may use the bindActionCreators() helper from Redux.
If ownProps is specified as a second argument, its value will be the props passed to your component, and mapDispatchToProps will be re-invoked whenever the component receives new props.
function mapDispatchToProps(dispatch) {
return { actions: bindActionCreators(actionCreators, dispatch) }
}
If you omit it, the default implementation just injects dispatch into your component’s props.
then in component:
this.props.dispatch(someActionCreator());
source react-redux DOCS
Therefore, the benefit of injecting object of actionCreators over a function, is more convenient and concise form of passing actionCrators to component

Related

DispatchToProps() method in React Redux

I've been going through some react - redux code and I'm stuck on the below code snippet
const mapDispatchToProps=dispatch=>({
addComment: (dishId, rating, author, comment) => dispatch(addComment(dishId, rating, author, comment))
})
I'm not that experienced in Javascript so it would be good to get a clarification on what this mapDispatchToProps method is accomplishing.
I know what this method is, I'm just confused on the Javascript part
mapDispatchToProps is the second argument of the connect function in redux. mapStateToProps is the first. You define mapDispatchToProps as a function of dispatch, which returns an object. The syntax const something = dispatch => ({ ... }) is a shorthand for an arrow function which returns an object. It is almost the same as
function mapDispatchToProps(dispatch){
return {
addComment: (dishId, ...) => dispatch(addComment(dishId, ...))
}
}
When you feed mapDispatchToProps to connect, and then wrap your component in that connect, it injects the properties and methods of the returned object into the component, as props. Hence the name mapXToProps. It will usually be used like this:
export default connect( mapStateToProps, mapDispatchToProps )(Component)
If you don't have a mapStateToProps, use null in its place. mapDispatchToProps allows you to inject your redux actions into your component, so that you can affect the redux store through the props on your component. mapStateToProps is similar - it allows you to grab items from your redux store, and assign them to props on your component. In your example, you would now be able to access this.props.addComment as a prop on your wrapped component. This is how you connect your components to the redux store, and how you can send and recieve data to the store.
Note that these are just naming conventions. You can actually name these functions whatever you want - but most people use mapStateToProps and mapDispatchToProps, as its pretty descriptive of what's hapenning. You can name them puppy and booya for all redux cares, so long as you feed them to connect properly.
Obligatory read the docs. Though to be frank when I was learning this, someone had to actually explain it to me too, but the docs are a great resource.

Understanding mapStateToProps & mapDispatchToProps in React-Redux

I'm trying to understand the connect() method of react-redux. Usually it takes two function as argument: mapStateToProps() & mapDispatchToProps(). I write a example for myself, here is connect() section of my User component:
//imports...
class User extends Component {
/* constructor, JSX, other functions... */
}
const mapStateToProps = (state) => {
return {
users: state.UserReducer
};
};
const mapDispatchToProps = (dispatch) => ({
deleteUser: (id) => dispatch(deleteUser(id))
});
export default connect(mapStateToProps, mapDispatchToProps)(User);
According to Docs I have taken the following two conclusions about mapStateToProps() & mapDispatchToProps():
mapStateToProps(): it makes that state available in our component. i.e. it is used to pass reducer to component.
mapDispatchToProps(): it maps component relevant functions to action functions, i.e. with this function We can perform the action that we want in our component.
is my conclusions correct?
React components accept data from outside via props.
maptStateToProps and mapDispatchToProps, literally, pass the selected state properties and actions that are needed inside your component as props.
The state values and actions passed to the component are available in the props of the component.
In your example, you can use this.props.users or this.props.deleteUser().

Connect actions to functions

Recently I've been using react classes and using mapStateToProps and the react-redux connect, i've been able to use actions within my classes because of this, however now I'm trying to write functions that don't belong to a class like the following
import { some_imported_action } from '../actions/my_actions';
export function f() {
some_imported_action();
}
But I'm having trouble trying to get the function to run the action imported, I assume the problem is trying to use the react-redux connect with a function instead of a class. When I run the code above nothing happens, no errors but the action doesn't execute.
I've tried importing prop-types and making the function required with
some_imported_action: PropTypes.func.isRequired
The errors I get from trying to run it are things to do with either 'this', or 'props' is undefined in the line.
this.props.some_imported_action();
I think it might not even be possible without a class, any ideas?
Thanks
When your f() function is called nothing happens with Redux because the action creator (some_imported_action) is not connected to your store.
Redux actions needs to be dispatched using the store.dispatch function in order to be handled by reducers.
The connect function from react-redux receive as second argument a mapDispatchToProps object or function:
[mapDispatchToProps(dispatch, [ownProps]): dispatchProps] (Object or Function):
If an object is passed, each function inside it is assumed to be a
Redux action creator. An object with the same function names, but with
every action creator wrapped into a dispatch call so they may be
invoked directly, will be merged into the component’s props.
If a function is passed, it will be given dispatch as the first parameter. It’s up to you to return an object that somehow uses dispatch to bind action creators in your own way.
Example passing a callback prop called someAction binded with store dispatch:
import { connect } from 'react-redux';
const someAction = () => ({ type: 'SOME_ACTION' });
function MyComponent({ someAction }) {
return <div onClick={someAction}>...</div>;
}
export default connect(null, { someAction })(MyComponent);
Read more info related to React-Redux connect

How do I avoid re-rendering a connected React PureComponent due to mapDispatchToProps functions?

whenever mapDispatchToProps is called, it generates new functions to return. For example:
const mapDispatchToProps = function(dispatch, ownProps) {
return {
addToStack: (episodeId, stackId) => {
return dispatch(StackAction.addEpisodeToStack(episodeId, stackId));
},
};
}
Every time the mapDispatchToProps is called, it will generate a new object, with a new arrow function.
In my application I often have to avoid re-rendering my components. Using a PureComponent is often the way to go. However, since the functions are always different, PureComponent won't help and I'd have to create a shouldComponentUpdate strategy. There, I'd have to "blacklist" all of the mapDispatchToProps functions and ignore all of them. I'd have to add every new function to the list so it'd avoid re-rendering.
here is an example of the blacklist shouldComponentUpdate boilerplate:
const blacklist = [
'addToStack',
]
shouldComponentUpdate(nextProps, nextState) {
for (let i in nextProps) {
if (blacklist.includes(i)) continue;
if (nextProps[i] !== this.props[i]) {
return true;
}
}
for (let i in nextState) {
if (nextState[i] !== this.state[i]) {
return true;
}
}
return false;
}
I've come up with a new solution
const dispatch;
const mapDispatchToPropsFunctions = {
addToStack: (episodeId, stackId) => {
return dispatch(StackAction.addEpisodeToStack(episodeId, stackId));
},
};
const mapDispatchToProps = function(dispatchArg, ownProps) {
dispatch = dispatchArg
return mapDispatchToPropsFunctions;
}
this way the functions are constant and won't trigger a re-rendering of a PureComponent and I don't have to maintain a shouldComponentUpdate function.
however this seems wrong to me.
Is there a "default" way of dealing with this problem?
I generally advise to not try to recreate functions that capture props values like that, but instead have a handler method on your class that passes prop values to the action creator. I also advise that people not write mapDispatch functions directly, but use the "object shorthand" for connect().
Example:
const actions = {addToStack : StackAction.addEpisodeToStack};
class MyComponent extends React.Component {
addToStack = () => {
this.props.addToStack(this.props.episodeId, this.props.stackId);
}
}
In your specific snippet, it looks like you're not even referencing any props values in mapDispatch anyway, so there was no need to declare the ownProps parameter. (connect will only call a mapDispatch function multiple times if the ownProps parameter is requested. Otherwise, it only calls mapDispatch once, when the component is created.)
Turns out that the actual solution is to not use mapDispatchToProps at all.
In the react-redux docs there is a reference to a shorthand method:
If an object is passed, each function inside it is assumed to be a
Redux action creator. An object with the same function names, but with
every action creator wrapped into a dispatch call so they may be
invoked directly, will be merged into the component’s props.
So the solution is to, instead of providing a function that binds dispatch to your action creators, just provide to connect an object containing your action creators as second argument. Connect will only bind them once and use the same functions all the time, avoiding unnecessary re-renders.
This should be in fact the preferred way of supplying action creators to connect.

Should mapDispatchToProps dispatch initialization actions?

Suppose a stateless, functional UserProfile component that displays user data for the given url. Suppose it is being wrapped with connect(mapStateToProps, mapDispatchToProps)(UserProfile). Finally, suppose a reducer that reduces into state.userProfile. Anytime the url changes, I need to re-initialize the state.userProfile, so a solution that comes to mind is to do so from within the mapDispatchToProps like so:
function mapDispatchToProps(dispatch, ownProps) {
dispatch(fetchUser(ownProps.userId))
return {
...
}
}
Provided that the thunked fetchUser ignores repeated calls by comparing with current state, is this an acceptable practice? Or are there problems associated with calling dispatch immediately from this map function?
This is unsupported and can break at any time.
mapDispatchToProps itself should not have side effects.
If you need to dispatch actions in response to prop changes, you can create a component class and use lifecycle methods for this:
class UserProfile extends Component {
componentDidMount() {
this.props.fetchUser(this.props.id)
}
componentDidUpdate(prevProps) {
if (prevProps.id !== this.props.id) {
this.props.fetchUser(this.props.id)
}
}
// ...
}

Categories

Resources