Dispatch method in ReactJS - reactjs

I was basically building an authentication system with reactJS by going through some references online.
I got really confused by the code segment below
import React, { Component } from "react";
import { Link, withRouter } from "react-router-dom";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { registerUser } from "../../actions/authActions";
.
.
.
const newUser = {
name: this.state.name,
email: this.state.email,
password: this.state.password,
password2: this.state.password2
};
this.props.registerUser(newUser, this.props.history);
};
.
.
.
const mapStateToProps = state => ({
auth: state.auth,
errors: state.errors
});
export default connect(
mapStateToProps,
{ registerUser }
)(withRouter(Register));
So basically, registerUser is an action, but instead of invoking it through the dispatch method, it's accessed by this.props? Why is that?
Also, registerUser is passed as an argument to the connect() method, why?

That is a common pattern with redux. connect takes two arguments. The first maps the state and the second maps the dispatch actions. By using connect to wrap your component, both state and actions will be available through the props to your component.
It's a bit easier to understand when you actually map the dispatch calls:
const mapStateToProps = state => ({
auth: state.auth,
errors: state.errors
});
const mapDispatchToProps = dispatch => {
return {
registerUser: (user) => dispatch(registerUser(user))
}
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(withRouter(Register));

This is called mapDispatch to props. You can pass it as 2nd argument to connect funtion.
mapDispatchToProps is called with dispatch function as an argument
const mapDispatchToProps = dispatch => {
return {
registerUser: (user) => dispatch(registerUser(user))
}
};
and then pass it to connect function as 2nd argument
export default connect(
mapStateToProps,
mapDispatchToProps
)(withRouter(Register));
The above can be simplified, by using a shorthand (Recommended by react-redux library)
Shorthand
Shorthand is by simply passing the actions in an object (in this case the registerUser)
export default connect(
mapStateToProps,
{ registerUser }
)(withRouter(Register));
For more info https://react-redux.js.org/using-react-redux/connect-mapdispatch

Related

mapDispatchToProps doesn't add actions to component

I'm tryng to use connect like in Redux docs (here) and I'm also using redux toolkit to work with redux (link)
import { connect } from 'react-redux'
import { actions } from '../../redux/actions';
import FormComponent from './Form';
const mapStateToProps = (state: any) => ({
item: 'state.item'
})
const mapDispatchToProps = { actions }
export default connect(
mapStateToProps,
mapDispatchToProps
)(FormComponent)
In my FormComponent I'm getting item props from mapStateToProps but actions are not passed down
My actions look like this
import { createAction } from '#reduxjs/toolkit';
const registerUser = createAction<Object, 'REGISTER_USER'>('REGISTER_USER');
export const actions = {
registerUser
}
And if I do console.log(actions) it is an object with function.
All the help will be appreciated.
This line:
mapDispatchToProps = { actions }
Is the equivalent to:
mapDispatchToProps = {
actions: {
registerUser
}
}
As per the docs bindActionCreators expects an object hash of action creators. In this example, you have nested yours under an actions property which (based on the source) will result in them being excluded from the returned props.
If you want the actions to be accessible directly from props then you can set mapDispatchToProps = actions, or just pass actions directly into connect e.g.
connect(mapStateToProps, actions)(FormComponent)

What does this syntax mean export default connect(mapStatetoProps, mapDispatchToProps)(LandingComponent)

A module I have contains the following line. connect seems to have two pairs of parentheses. What does this mean?
export default connect(mapStatetoProps, mapDispatchToProps).
(LandingComponent)
Tried to lookup various documentation
import { connect } from 'react-redux'
import { LandingComponent } from './Landing'
const mapStatetoProps = state => {
return {}
}
const mapDispatchToProps = dispatch => {
return {}
}
export default connect(mapStatetoProps, mapDispatchToProps)
(LandingComponent)
Expect to understand what the syntax means.
The second set of parentheses is because connect(..., ...) returns a function. This function is a component decorator which is why it is called with the landing component class.
If you split it up it might become clearer:
const decorate = connect(mapStatetoProps, mapDispatchToProps);
const ReduxConnectedLandingComponent = decorate(LandingComponent);
export default ReduxConnectedLandingComponent;
In this case decorate is a function that accepts a single component and returns a component. i.e. it takes the plain component and returns a smarter one which pulls props from the nearest provided store in the hierarchy.
Example:
const TodoItem = ({ todo, destroyTodo }) => {
return (
<div>
{todo.text}
<span onClick={destroyTodo}> x </span>
</div>
)
}
const mapStateToProps = state => {
return {
todo: state.todos[0]
}
}
const mapDispatchToProps = dispatch => {
return {
destroyTodo: () =>
dispatch({
type: 'DESTROY_TODO'
})
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(TodoItem)
mapStateToProps and mapDispatchToProps are both pure functions that are provided the stores “state” and “dispatch” respectively. Furthermore, both functions have to return an object, whose keys will then be passed on as the props of the component they are connected to.
In this case, mapStateToProps returns an object with only one key : “todo”, and mapDispatchToProps returns an object with the destroyTodo key.
The connected component (which is exported) provides todo and destroyTodo as props to TodoItem.
Source: https://www.sohamkamani.com/blog/2017/03/31/react-redux-connect-explained/

this.props... is not a function react react-redux

I have a problem with dispatching a action from componentDidMount...
error is : TypeError: this.props.postDetails is not a function
Action.js
export const postDetails = data => ({
type: "POST_DETAILS",
post: data
})
Container/GetDetails.js
import Details from '../components/Details'
import { postDetails } from '../actions'
const mapStateToProps = state => ({ post: state.post });
const mapDispatchToProps = dispatch => bindActionCreators({postDetails}, dispatch);
const GetDetails = connect(
mapStateToProps,
mapDispatchToProps
)(Details)
export default GetDetails
Component/Details.js
import React from 'react'
import { postDetails } from '../actions'
class Details extends React.Component {
constructor(props){
super(props);
}
componentDidMount() {
console.log("did mount details");
this.props.postDetails();
}
render() {
return (
<div>
Details page
</div>
)
}
}
export default Details;
Can someone help me? Why i have this error?
In App.js (or wherever you are importing the Details component), are you using the path to your GetDetails container (not component)?
I moved state from a component to a container and forgot to update the import path which gave me this same error. Updating the import path to the container took care of it.
Edit:
For example, I have an apiLanding folder that has apiLanding.js (the component) and apiLanding-container.js (the container).
In my app.js, I needed to change
import apiLanding from './components/apiLanding/apiLanding';
to
import apiLanding from './components/apiLanding/apiLanding-container';
That way, the app now has access to the redux state and actions. This was a silly mistake and may not be your issue, but wanted to share just in case the import path was overlooked.
You have to return an object, where the keys are your props. See docs.
const mapDispatchToProps = dispatch => ({ postDetails: bindActionCreators({postDetails}, dispatch) })
Or, you can use the shorthand notation:
const GetDetails = connect(
mapStateToProps,
{ postDetails }
)(Details)
I don't see bindActionCreator imported. Use eslint to get rid of these errors
There are two things which don't really seem right to me. Personally I never used bindActionCreators. I would just create my mapDispatchToProps as following:
const mapDispatchToProps = dispatch => {
return {
postDetails: () => dispatch(actions.postDetails)
};
};
But also your postDetails call expects an argument, which you should add in your function call. So your mapDispatchToProps would look like this:
const mapDispatchToProps = dispatch => {
return {
postDetails: (data) => dispatch(actions.postDetails(data))
};
};
Also you're importing your action as postDetails. Are you sure that this is just one action? And not a combination of all actions in your file? Note how I added your function as actions.postDetails instead of just postDetails.

Multiple actionCreators in single component

I want to have multiple actionCreators dispatched into one component. I know you can do this with state
export default connect(
(state: ApplicationState) => Object.assign({}, state.search, state.resources),
ResourcesState.actionCreators// i have another actionCreator I want to add
)(Home) as typeof Home;
But not sure the syntax to do this with actionCreators. I have read into
mapDispatchToProps
But not sure how to implement.
There are a few ways to set up dispatching of Redux actions in React components:
Use connect(mapState)(MyComponent). By default, your component will be given props.dispatch, and you can call props.dispatch({type : "SOME_ACTION"}).
Pass a mapDispatchToProps function as the second argument to connect. Inside, you can create new function references that call dispatch:
function mapDispatchToProps(dispatch) {
return {
addTodo : (text) => dispatch({type : "ADD_TODO", text})
}
}
You can also use the Redux bindActionCreators utility instead:
function mapDispatchToProps(dispatch) {
return bindActionCreators({addTodo, toggleTodo}, dispatch);
}
Finally, you can pass an object full of action creators directly to connect:
const actions = {addTodo, toggleTodo};
export default connect(mapState, actions)(MyComponent);
I highly recommend the fourth approach, which I also talk about in my blog post Idiomatic Redux: Why Use Action Creators?.
mapDispatchToProps is the second argument in connect. So for example:
import customAction from 'actions-directory-in-your-app'
const mapStateToProps = () => {} // no implementing anything for example purposes
const mapDispatchToProps = () => ({ customAction })
const ConnectedContainer = connect(
mapStateToProps,
mapDispatchToProps
)(YourContainer)
customAction becomes now a prop in YourContainer so you can use it the same way other props within your component.
the Second argument to connect takes an object or a function so you can add
export default connect(
(state: ApplicationState) => Object.assign({}, state.search, state.resources),
{
ResourcesState.actionCreators,
some_other_action_creators,
some_more
}
)(Home) as typeof Home;
Also read through this answer on Stackoverflow for more information on how to use action creators
Why is there no need for a mapDispatchToProps function here?
The second argument to connect takes an object, so you can use of ES6 syntax and avoid the use of mapDispatchToProps.
import { yourAction } from './your_actions_folder'
class Home extends Component{
....
//For dispatch a action you only call the action Creator
this.props.yourAction()
}
export default connect(mapStateToProps,{yourAction})(Home)

Access props in an ES7 decorator

I'm working on asynchronously validating my redux-form. But the example doesn't really show how to use redux state, to send off an action and get the result from the state. So how can I access the props from #connect in the reduxForm decorator to achieve this?
#connect(
state => (...)
dispatch => ({
actions: auth
})
)
#reduxForm({
form: 'auth.signup',
fields,
(values, dispatch) => ({
// dispatch validation action here
})
})
Also, putting the function directly in the decorator throws up a syntax error but the logic has to be within there to have access to the props, right?
You don't need to connect again. Redux-form allow you to pass mapStateToProps and mapDispatchToProps as the second and third parameter. So you just need,
#reduxForm({
form: 'auth.signup',
fields,
(values, dispatch) => ({
// dispatch validation action here
})
}, mapStateToProps, mapDispatchToProps).
mapStateToProsp and mapDispatchToProps both take in props of the wrapped component as the second parameter.
The second parameter to both mapStateToProps and mapDispatchToProps is an object representing the props passed to the component.
The convention is to call this parameter ownProps:
#connect(
(state, ownProps) => ...,
(dispatch, ownProps) => ...
)
The redux-form documentation states that its map*ToProps functions should be the same.
My question wasn't really about where to put the map*ToProps functions, I was just blind to the fact that redux-form gives you a dispatch parameter which allows you to bind the action creator again purely for the use of running actions for validation.
It also requires moving the function out of the decorator into a constant just like the example linked in the question.
Here's a full example for anybody interested:
import React, { Component, PropTypes } from 'react';
import { bindActionCreators } from 'redux';
import { reduxForm } from 'redux-form';
import * as auth from 'actions/auth';
const fields = [
'username',
'password'
];
const asyncValidate = (values, dispatch) => {
return new Promise((resolve, reject) => {
if(values.username) {
let authActions = bindActionCreators(auth, dispatch);
authActions.checkUsernameValid(values.username);
resolve();
}
});
};
#reduxForm(
{
form: 'auth.login',
fields,
asyncValidate,
asyncBlurFields: [ 'username' ]
},
state => ({
usernameValid: state.auth.usernameValid,
usernameValidError: state.auth.usernameValidError
}),
dispatch => ({
authActions: bindActionCreators(auth, dispatch)
})
)
export default class Login extends Component {
// Component here which has access to authActions
// if the form successfully submits.
}

Resources