When using connect from react-redux, if I do mapDispatchToProps before mapStateToProps, the component fails to render stating dispatch is not a function:
function mapDispatchToProps(dispatch) {
return {
getData: () => {
dispatch(getSomething())
}
}
}
function mapStateToProps(state) {
return {
somevalue: state.somevalue,
}
}
export default connect( mapStateToProps, mapDispatchToProps ) ( Comp );
//export default connect( mapDispatchToProps, mapStateToProps ) ( Comp ); THIS FAILS
Why does the sequence for mapStateToProps and mapDispatchToProps matter?
As it state in react-redux documentation for connect, it waits for 4 different parameters.
If you change the order of parameters your functions will fire with wrong parameters and this will cause your functions to fail.
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
Connects a React component to a Redux store. connect is a facade
around connectAdvanced, providing a convenient API for the most common
use cases.
It does not modify the component class passed to it; instead, it
returns a new, connected component class for you to use.
Related
I have a class component that I want to connect to the store in order to pull data.
I have tested the action with a function component and it was working well. Due to some limitations, I decided to use a class component.
The problem is that 'getTableDataAction' does not exist despite the fact that I did connect the dispatch via connect function.
As a result I will get the foolowing error:"TypeError: this.props.getTableDataAction is not a function"
export default class TchExportButton extends Component {
constructor(props) {
super(props);
this.state = {
selectedIndex: 0,
anchorEl: null,
options: ['Select drill down report',
'Destroyed DCA',
'Repaired - A008']
}
}
componentDidUpdate(prevState) {
if (this.state.selectedIndex !== prevState.selectedIndex) {
if (this.state.selectedIndex > 0) {
this.props.getTableDataAction(this.state.selectedIndex)
}
}
}
.....
...
..
const mapStateToProps = (state) => {
return {
data: state.tchCommecialTableData.data,
}
}
const mapDispatchToProps = (dispatch) => {
return {
getTableDataAction: (buttonId) => dispatch(engineeringDataAction.getTableDataAction(buttonId))
}
}
connect(mapStateToProps, mapDispatchToProps)
I am not sure what I am doing wrong but something is missing there and I can't find the root cause.
Any idea what is wrong?
Thank you
Your default export should be the connected component, not the unconnected component.
export default connect(mapStateToProps, mapDispatchToProps)(TchExportButton);
(Make sure to remove the export default at the top)
Define class and export connect(class):
class TchExportButton extends Component
export:
export default connect(mapStateToProps, mapDispatchToProps)(TchExportButton)
To add to what the other guys have answered, connect is a function that returns a function which in turn returns a component. In react-redux, connect is probably something like this:
function connect(mapStateToProps, mapDispatchToProps, ...other arguments){
return function(Component){
...
}
}
It's definitely more complicated than this but this is the core concept of calling
connect(mapStateToProps, mapDispatchToProps)(Component)
as the first function call calls the first function and the second calls the returned function.
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/
function mapStateToProps(state) {
return { todos: state.todos }
}
function mapDispatchToProps(dispatch) {
return { actions: bindActionCreators(actionCreators, dispatch) }
}
export default connect(mapStateToProps, mapDispatchToProps)(TodoApp)
why would i use mapDispatchToProps and bindActionCreators as second prop if
export default connect(mapStateToProps, { getSthAction })(TodoApp)
this way it works as well? is there any difference?
The only difference in those examples is that your mapDispatch function will result in this.props.actions.someAction(), rather than this.props.someAction(), because you're explicitly returning the result of bindActionCreators as a field called actions.
I personally recommend always using the "object shorthand" version - to me, there's never a good reason to actually write a separate mapDispatch function.
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)
I am working on a first React-Redux app. I have the following container:
import { connect } from 'react-redux'
import Visualization from '../components/visualization'
// http://redux.js.org/docs/basics/UsageWithReact.html#implementing-container-components
const mapStateToProps = (state) => state; // identity transform, for now...
function mapDispatchToProps(dispatch) {
return {
stepForward: () => dispatch('STEP_FORWARD')
};
}
const VisualizationContainer = connect(
mapStateToProps,
mapDispatchToProps
)(Visualization);
export default VisualizationContainer;
And the following subsequent component:
import React from 'react'
export default class Visualization extends React.Component {
render() {
console.log(this.props.stepForward());
return <div>"HELLO WORLD"</div>;
}
}
However, when I run this application I error out with:
Uncaught Error: Actions must be plain objects. Use custom middleware for async actions.
The problem, marked with XXX, is that stepForward exists, but explodes when executed. What is the error here?
It is just as the error says. An action should be an object and no other type. You're trying to dispatch a string as an action, which is invalid. Instead, you should use an object with a type property set to the action's type string.
Change your mapDispatchToProps function to:
function mapDispatchToProps(dispatch) {
return {
stepForward: () => dispatch({
type: 'STEP_FORWARD'
})
};
}
and it should work.
You should actually go a bit further than that and make all your actions follow a standardized template. A nice one a lot of React devs follow is flux-standard-action.