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().
Related
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.
I'm getting my head around Redux and while I understand how it functions I'm not entirely sure if I'm using it correctly.
The Redux Documentation gives plenty of example on how to connect a functional component to my actions. But is there any way to call my action directly from within a function?
Is it possible to get something like this to work?
function mapDispatchToProps(dispatch) {
return {
saveUserData: user => dispatch(saveUserData(user))
};
}
function ConnectedUserInfo(){
console.log("fetching user info")
fetch('endpoint', 'headers',
}).then(response =>
response.then(user => {
this.props.saveUserData(user)
})
)
}
const getUserInfo = connect(
null,
mapDispatchToProps
)(ConnectedgetUserInfo);
export default getUserInfo;
I tried setting my redux state directly with saveUserData(user) but couldn't get the Store to change.
Connecting the two doesn't seem to do anything, unless I'm doing something wrong.
I'm unsure if this is the solution I'm looking for or if Redux wants me to mapDispatchToProps every time I want to change the state.
if you read the react redux documentation , you can see that connect method returns an HOC which accepts only react components.
in your code ConnectedgetUserInfo is not a react compoenent.
react-redux documentation: react-redux
react component defintion: https://reactjs.org/docs/react-component.html
also you have to name react component with starting character Capital.
I recommend instead of mapdispatch or mapstate use useDispatch and useSelector from react-redux for functional components
import {useSelector,useDispatch} from 'react-redux;
const dispatch=useDispatch();
const stateSelector=useSelector(state=>({
your states
}));
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.
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
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)
}
}
// ...
}