Multiple actionCreators in single component - reactjs

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)

Related

How to use redux-toolkit createSlice with React class components

I've started using the redux-toolkit slicers in functional components, (example from react-redux example)
slicer:
export const counterSlice = createSlice({
name: 'counter',
initialState: {
value: 0,
},
reducers: {
increment: state => {
state.value += 1;
},
decrement: state => {
state.value -= 1;
},
incrementByAmount: (state, action) => {
state.value += action.payload;
},
},
});
use in component:
const count = useSelector(selectCount);
const dispatch = useDispatch();
return (
<button
className={styles.button}
aria-label="Increment value"
onClick={() => dispatch(increment())}
>
)
my question is how can I use this slicer in the class component since I cant use hooks inside them.
I've tried using connect (from redux) but I can't find a way to "stitch" the actions and selectors from the slicer to my component.
I couldn't find any documentation on this as well.
Class vs. function components and redux-toolkit vs "vanilla" redux are two independent decisions that don't have any impact on each other. (Though you should be aware that function components and hooks are recommended over class components for everything React).
I've tried using connect (from redux) but I can't find a way to "stitch" the actions and selectors from the slicer to my component.
How do the docs "stitch" the actions and selectors when using useDispatch and useSelector? Do that, but with the connect higher-order component instead.
The increment() function in the docs example that you posted doesn't just magically exist, it needs to be imported from the slice. You can export the entire actions object and use actions.increment but you usually see the actions exported as individual variables.
From the docs:
Most of the time, you'll probably want to use ES6 destructuring syntax to pull out the action creator functions as variables, and possibly the reducer as well:
Your slice file might look like this:
const counterSlice = createSlice( /* same as before */ );
// destructure actions and reducer from the slice (or you can access as counterSlice.actions)
const { actions, reducer } = counterSlice;
// export individual action creator functions
export const { increment, decrement, incrementByAmount } = actions;
// often the reducer is a default export, but that doesn't matter
export default reducer;
The first argument of connect is mapStateToProps, where you use selectors (either inline arrow functions state => state.something or selector functions that you import) to create an object of props from the state. That might look like:
const mapStateToProps = (state) => ({
count: state.counter.value
});
The second argument mapDispatchToProps is optional. If you pass an object with your action creators, your component will receive versions of those action creators that are already bound to dispatch. You would be able to call this.props.increment() directly rather than this.props.dispatch(increment()). You will see this syntax commonly used in tutorials with connect.
import React from "react";
import { connect } from "react-redux";
import { increment, decrement } from "./counterSlice";
class MyComponent extends React.Component {
render() {
return (
<div>
<h1>Count is {this.props.count}</h1>
<button onClick={() => this.props.increment()}>
Increment
</button>
<button onClick={() => this.props.decrement()}>
Decrement
</button>
</div>
);
}
}
const mapStateToProps = (state) => ({
count: state.counter.value
});
const mapDispatchToProps = { increment, decrement };
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent);
If you leave off the mapDispatchToProps argument entirely, your component receives the raw dispatch function. You would call the dispatch on you imported action creators like this.props.dispatch(increment()). This syntax is more similar to how useDispatch is used. Both connect and useDispatch give you access to the dispatch function and you can call that function with an action that you create from an action creator function like increment() or decrement().
import React from "react";
import { connect } from "react-redux";
import { increment, decrement } from "./counterSlice";
class MyComponent extends React.Component {
render() {
return (
<div>
<h1>Count is {this.props.count}</h1>
<button onClick={() => this.props.dispatch(increment())}>
Increment
</button>
<button onClick={() => this.props.dispatch(decrement())}>
Decrement
</button>
</div>
);
}
}
const mapStateToProps = (state) => ({
count: state.counter.value
});
export default connect(mapStateToProps)(MyComponent);
Complete CodeSandbox

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/

How to replace bindActionCreators and to use mapDispatchToProps and mapStateToProps

We are calling an API using bindActionCreators and every time I need to use this.props to post and to get data.
The problem is that I need to store the response data in some variable and access it through this.props.variable. For example:
export default withStyles(styles, { withTheme: true })(connect(
state => state.sensi,
dispatch => bindActionCreators(actionCreators, dispatch)
)(FetchSensi));
How can I do it?
mapStateToProps
const mapStateToProps = state => ({
variableName: state.someVariableFromState
});
and i don't use mapDispatchToProps, i pass directly action to connect like this
import {action1, action2} from './someActionFolder';
export default connect(mapStateToProps, {action1, action2})(YourComponent);
and inside your component you can use your actions like
this.props.action1();
this.props.action2();
you can also combine all your actions like
import * as actions from './yourActionsFolder';
export default connect(mapStateToProps, actions);

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.

Why does the connect sequence matter?

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.

Resources