mapDispatchToProps doesn't add actions to component - reactjs

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)

Related

Dispatch method in 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

Replace useSelector with createStructuredSelector in react-native

I use reselect library with my react-native project. I have the following code.
import { createSelector } from 'reselect';
import { ApplicationState } from 'types/reducer.types';
const selectAuth = ({ auth }: ApplicationState) => auth;
export const selectFirstName = createSelector([selectAuth], auth => auth.user?.firstName);
export const selectLastName = createSelector([selectAuth], auth => auth?.user?.lastName);
In the container the code is like this.
import { connect } from 'react-redux';
// import { useSelector } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { signOut } from 'Redux/auth/auth.actions';
import { selectFirstName, selectLastName } from 'Redux/auth/auth.selectors';
import CustomDrawer from './custom-drawer.component';
const mapStateToProps = createStructuredSelector({
firstName: selectFirstName,
lastName: selectLastName,
});
export default connect(mapStateToProps, { signOut })(CustomDrawer);
Now I want to use useSelector hook. I tried several ways but it didn't work. How can this be done?
createStructuredSelector returns a selector (a function that takes state and returns an object {firstName, lastName}), so you can use it inside of useSelector
const {firstName, lastName} = useSelector(createStructuredSelector({
firstName: selectFirstName,
lastName: selectLastName,
}));
or using your existing variable
const {firstName, lastName} = useSelector(mapStateToProps);
However there's not really any point in combining and then destructing the properties when you can use many useSelector hooks in one component.
const firstName = useSelector(selectFirstName);
const lastName = useSelector(selectLastName);
useSelector and useDispatch are two hooks that can be imported from react-redux package and these two can easily replace the need for using connect.
Here is how you can use them:
import { useDispatch, useSelector } from 'react-redux';
import {someAction} from '../user/userActions';
const YourComponent = () => {
// useDispatch returns back a dispatch function
const dispatch = useDispatch();
// useSelector selects a portion of the state, it plays the role and
// replace the need for mapStateToProps in connect higher order function
const currentUser = useSelector(state => state.user.currentUser);
return(
<>
<h1>{ currentUser.firstName }</h1>
// Here you can use dispatch to dispatch an action
<button onClick={() => dispatch(someAction)}></button>
</>
)
};
export default YourComponent;
Overall, you can see that by using useSelector and useDispatch we removed so much boilerplate from our code(connect, mapStateToProps, mapDispatchToProps, createStructuredSelector removed) and it looks clean and more readable and still works as previous.
Here is the official resource you can read more about the two above-mentioned hooks.
React-Redux Hooks
P.S. You can also send a selector created using reselect or whatever selector of your choice into useSelector.

react redux dispatch fuction is not triggered

i attach all my required files below for references
when i try to call react redux dispatch fuction is not triggered. am new to react redux
action.js file
import { ITS_USER } from './types';
import {store} from './index'
export function addPerson(person) {
console.log("chek",person);
console.log("state",store.getState());
return dispatch => {
dispatch(adduser(person));
}
}
export function adduser(person){
return {
type: ITS_USER,
payload: person
};
}
Reducer.js
import { ITS_USER } from './types';
export default function(state=[], action) {
console.log("dxasdads",action.type)
switch (action.type) {
case ITS_USER:
return action.payload;
default:
return state;
}
}
triger function call
**var person="praveen"
addPerson(person)**
You have to connect your component with connect HOC provided by react-redux libary, then you can trigger a dispatch function by dispatching it from mapStateToProps passed as a first argument to connect function. Something like this:
import YOUR_COMPONENT from './PATH_TO_YOUR_COMPONENT'
const mapDispatchToProps = dispatch => ({
addPerson: (person) => dispatch(addPerson(person)),
});
export default connect(null, mapDispatchToProps)(YOUR_COMPONENT);
And then inside your component, you will get addPerson function as your component props. You can simply call it by this.props.addPerson(person).

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)

Map redux dispatch to redux-form

Sorry if this has been answered elsewhere, I'm a Redux beginner, and I couldn't find anything related.
I'm having trouble mapping dispatch to ReduxForm. Im setting up my reducers like this:
import { reducer as formReducer } from 'redux-form';
import foo from './foo';
import bar from './bar';
const rootReducer = combineReducers({
foo,
bar,
form: formReducer
});
export default rootReducer;
and then combining them like so:
import rootReducer from '../reducers/index';
const loggerMiddleware = createLogger();
const createStoreWithMiddlware = applyMiddleware(
thunkMiddleware,
loggerMiddleware
)(createStore);
export default function configureStore(initialState) {
const store = createStoreWithMiddlware(rootReducer, initialState);
return store;
}
and then mapping dispatch to props like this:
function mapStateToProps(state) {
return {
foo: state.foo,
bar: state.bar
}
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(Actions, dispatch),
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(App)
And then I have a form component that I am wrapping in a reduxForm like this:
TestForm = reduxForm({
form: 'testForm',
fields: ['firstName', 'lastName', 'email']
})(TestForm);
export default TestForm;
But I get an error:
Warning: Failed propType: Required prop dispatch was not specified in ReduxForm(TestForm). Check the render method of TestFormParent.
What is the best way to inject dispatch into a form component?
Turns out the docs on npm are a little bit different, and more complete. If anyone else runs into this issue, use connectReduxForm instead of reduxForm solved the problem.
reduxForm works for version 4 of redux-form

Resources