Pass parameters to mapDispatchToProps() - reactjs

I can't lie, i'm a bit confused about react-redux. I think lot of the actions require parameters (for an example deleting items from the store), but even if i'm still reading about how to dispatch from component in that way to pass a parameter, about 2 hours now, i didn't get any answers. I was tried with this.props.dispatch and with mapDispatchToProps, i always get the this.props... is not a function message. Here's what i'm trying to do:
const getTheArray = (array) => {
return {
type: 'GET_ARRAY',
array
}
}
class Example extends......
componentDidUpdate(){
//i have a button, and when it clicked, turns the status: 'deleted'
if (this.state.status === 'deleted'){
fetch('http://localhost:3001/deleteitem', {
method: 'post',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
id: this.props.id
})
})
.then(res => res.json())
.then(data => this.props.deleteFromArray(data))
.catch(err => console.log(err))
}
}
function mapStateToProps(state) {
return {
array: state.array
};
}
function mapDispatchToProps(dispatch) {
return({
deleteFromArray: (array) => {dispatch(getTheArray(array))}
})
}
export default connect(mapStateToProps, mapDispatchToProps)(Example);
It isn't the only place where i need to dispatch with an action, where the action's object's properties depending on another property passed to the function, so i really want to do, what's the best way to pass property to action, and dispatch it within a react component.

import { bindActionCreators } from "redux"
function mapDispatchToProps(dispatch) {
return(bindActionCreators({
deleteFromArray: (array) => {getTheArray(array)}
}, dispatch))
}
In your dumbComponents, just call e.g this.props.deleteFromArray([1,2,3,4])
This should solve the issue. you are not binding with dispatch

use of react-redux
you need to import action first
import { storeProfiles } from 'actions/profiles';
define props
const {
storeProfiles
} = props;
get data use of userEffect
useEffect(() => {
fetchProfiles().then(storeProfiles);
}, [ fetchProfiles, storeProfiles ]);
map to props
const mapDispatchToProps = dispatch => ({
storeProfiles: x => dispatch(storeProfiles(x))
});
export default connect(mapStateToProps, mapDispatchToProps)(Component);
Read official documentation

Using es6:
const mapDispatchToProps = (dispatch) => ({ addToCart: (params) => dispatch(actions.addToCart(params)), });

Actually, you don't have to pass params to actions like that, in my opinion.
I'm using React Hooks with TypeScript and Redux and thunk. The way I delete a post using an action creator looks like this:
PostDetails.tsx component:
import {deletePost, getPost, LanguageActionTypeEnum} from "../../../../app/store/aspian-core/actions";
interface IPostDetailsProps {
getPost: Function;
// action for deleting a post
deletePost: Function;
loadingInitial: boolean;
submitting: boolean;
post: IPost | undefined;
lang: LanguageActionTypeEnum;
}
const PostDetails: FC<PostDetailsProps> = ({
match, getPost,
lang, loadingInitial,
submitting, deletePost, post
}) => {
}
// To delete a post
const onDeleteBtnClick = async (id: string) => {
try {
/// using deletePost action and passing id to it
await deletePost(id);
message.success(t('messages.post-deleting-success'));
} catch (error) {
message.error(t('messages.post-deleting-error'));
}
};
return (
// omitted for brevity
<Popconfirm
key={uuidv4()}
title={t('popconfirm.title')}
/// calling onDeleteBtnClick() here
onConfirm={() => onDeleteBtnClick(post!.id)}
okText={t('popconfirm.ok-text')}
cancelText={t('popconfirm.cancel-text')}
placement={lang === LanguageActionTypeEnum.en ? 'left' : 'right'}
okButtonProps={{
danger: true,
}}
>
<Button
loading={submitting}
size="small"
type="primary"
icon={<DeleteFilled/>}
danger
>
{t('buttons.delete')}
</Button>
</Popconfirm>
// omitted for brevity
)
}
// Redux State To Map
const mapStateToProps = ({post, locale}: IStoreState): { post: IPost | undefined, submitting: boolean, loadingInitial: boolean, lang: LanguageActionTypeEnum } => {
return {
post: post.post,
submitting: post.submitting,
loadingInitial: post.loadingInitial,
lang: locale.lang
}
}
// Redux Dispatch To Map
const mapDispatchToProps = {
getPost,
// mapped deletePost action to props here
// and I will be able to pass id to its argument later
deletePost
}
export default connect(mapStateToProps, mapDispatchToProps)(PostDetails);
and my the action creator looks like this:
postActions.ts:
export const deletePost = (id: string) => async (dispatch: Dispatch) => {
try {
dispatch(setPostSubmitting(true));
await agent.Posts.delete([id]);
dispatch<IDeletePostAction>({
type: PostActionTypes.DELETE_SINGLE_POST,
payload: {
id,
submitting: false
}
})
} catch (error) {
console.log(error);
dispatch(setPostSubmitting(false));
throw error;
}
}
And it works fine. Hopefully it could help you with your situation.

If you use actions as arrow function so there would be no need to bindActionsCreators, let me do it with a very simple way.
your action should be of arrow function like so:
export const formHandler=({key,value})=>{
/*for checking that you get the values on function call or not*/
console.log(key,value)
return {
type:'formHandler',
payload:{key,value},
}
}
the below action function takes to arguments so you have to pass likewise
const mapDispatchToProps = dispatch=> {
/*this will add the functions as a props to the components
which can be trigger on order to change the state*/
return {
inputHandler: ({key,value})=>dispatch(formHandler({key,value}))
}
}
at the end you can use connect on yourComponent
export default connect(mapStatesToProps,mapDispatchToProps)(yourComponent)
then in your component
you can call the function using
class component
this.props.inputHandler({key:'k',value:2})
functional component
props.inputHandler({key:'k',value:2})

Related

React-Redux action is not dispatched properly?

I have following Action:
import axios from 'axios';
export function getAPIData(id)
{
return (dispatch) =>
{
axios.get('http://localhost:5000/api/' + id)
.then(res =>
{
dispatch(
{
type: 'DONE',
payload: res.data
});
});
}
}
Then in my Component I`m dispatching the action:
componentDidMount()
{
this.props.dispatch(getAPIData());
}
And then:
function mapStateToProps(state)
{
console.log(state);
return {
data: state.result
};
}
export default connect(mapStateToProps)(Rows);
In console, when I try to find the payload, it says what is bellow.
function()
arguments: TypeError: 'arguments', 'callee', and 'caller' cannot be
accessed in this context.
caller: TypeError: 'arguments', 'callee', and 'caller' cannot be
accessed in this context.
length: 1
name: ""
Where is problem? Thanks a lot.
to dispatch an action you need to provide mapDispatchToProps .
First import your action
import { getAPIData } from "../store/actions/getAPIData";
then build mapDispatchToProps
const mapDispatchToProps = (dispatch) => {
return {
getAPIData: (props = null) => {
dispatch(getAPIData(props));
},
};
};
add this alongside mapStateToProps
export default connect(mapStateToProps , mapDispatchToProps)(Rows);
now you can call the action like this
componentDidMount()
{
this.props.getAPIData();
}

Why the error "Actions must be plain objects. Use custom middleware for async actions." appears, when there are no async actions?

I was trying to figure out React deeper and stuck on this error.
It didn't allow me to dispatch any actions. However, I'm not using async at all.
Here you can find codesandbox of the full app.
I've added thunkMiddleware to the store, so the app will work.
However I can't understand what is going on?
Here are the action creators, inside which I cloudn't dispatch.
I've searched for different similar answers, and all those were connected to incorrect usage of
async actions. Mine are sync:
import CART_ACTIONS from "../action_types/cartActionTypes";
function addToCart(item) {
return dispatch => dispatch({ type: CART_ACTIONS.ADD_ITEM, item: item });
}
function removeFromCart(item) {
return dispatch => {
dispatch({ type: CART_ACTIONS.REMOVE_ITEM, item });
};
}
function clearCart(item) {
return dispatch => {
dispatch({ type: CART_ACTIONS.CLEAR_CART });
};
}
export const cartActions = { addToCart, removeFromCart, clearCart };
You need to update your cartActions like this, if you don't want to use thunkMiddleware:
import CART_ACTIONS from "../action_types/cartActionTypes";
function addToCart(item) {
return({ type: CART_ACTIONS.ADD_ITEM, item: item });
}
function removeFromCart(item) {
return({ type: CART_ACTIONS.REMOVE_ITEM, item });
}
function clearCart(item) {
return({ type: CART_ACTIONS.CLEAR_CART });
}
export const cartActions = { addToCart, removeFromCart, clearCart };
Simply, you just need to return an action which must be an object with type property and optionally a payload.
codesandbox

How to access redux store from action creator?

I'm looking at trying to chain actions together. In my current issue, when the SET_CURRENT_USER action occurs, I'd like it to modify the state (set the current user) and then fire off a bunch of other side-effect tasks (go fetch data, rebuild the UI, etc). My first thought was "well, I'll set a listener on the store"... which resulted in this previous question: How to access 'store' in react redux? (or how to chain actions) There, I was basically that setting listeners is an anti-pattern.
The solution suggested was to 'dispatch' multiple actions chained together. I didn't follow how to do that (my "mapDispatchToProps" is based on the redux tutorials and looks nothing like the suggested mapDispatchToProps) so I did some additional googling about how to chain side-effect actions together and got to this page: https://goshakkk.name/redux-side-effect-approaches/
Trying the first example, I went to my action creator file, which looks like this:
(actionCreators.js)
export function setCurrentUser(username) {
return { type: SET_CURRENT_USER, payload: username }
}
export function actionTwo(username) {
return { type: ACTION_TWO, payload: username }
}
export function actionThree(username) {
return { type: ACTION_THREE, payload: username }
}
and I tried to change the 'setCurrentUser' action creator to something resembling what was in the demo but without the async parts for simplicity - just looking to see if the actions fire:
export function setCurrentUser(username) {
return dispatch => {
dispatch( { type: SET_CURRENT_USER, payload: username } );
dispatch( { type: ACTION_TWO, payload: username } );
dispatch( { type: ACTION_THREE, payload: username } );
}
}
In my app, my mapDispatchToProps looks like this:
const mapDispatchToProps = {
setCurrentUser: setCurrentUser,
}
and I call this in the switchUser handler, like this: this.props.setCurrentUser(this.state.newUsername);
... and I get an error saying that actions must be plain objects.
How can I chain actions together?
Perhaps a deeper issue is that I don't know how to access the store in order to be able to call store.dispatch. (which was my previous question noted above)
i cannot leave a comment so a question: why dont you just set mapState and pass the state as an argument to dispatched action?
Here are Component stuff
class AComponent extends Component{
...
... onClick={()=>this.props.anAction(this.props.state)}
}
const mapStateToProps = state => {
return { state: state.YourReducer }
}
const mapDispatchToProps = dispatch => {
return { anAction: () => dispatch(actions.doAnyStaffWith(state)) }
}
export default connect(mapStateToProps, mapDispatchToProps)(BeerReviewer)
actions file:
export function actionThree(state) {
return { type: ACTION_TYPE, state }
}
That's what you're looking for?
You'll need thunk in order to enhance your action creators. the reason why you're getting the "must be plain objects" error is because your action creator is returning a function(), not an object. Thunk allows you to return functions in your action creators and with it, the code you wrote should work.
import { createStore, combineReducers, applyMiddleware } from "redux"
import thunk from "redux-thunk"
const store = createStore(combineReducers({
data1: reducer1,
data2: reducer2
}), {}, applyMiddleware(thunk))
Hope that works.
You should be able to access your store like this (illustrating with a fetch request):
import { someAPICALL } from '../api'
export function setCurrentUser(username) {
return async (dispatch, getState) => {
const { yourStateVariable } = getState().YourReducer
, data = await someAPICall(yourStateVariable)
.catch(e => dispatch({ type: FETCH_ERROR, payload: e }))
if (data) {
dispatch( { type: SET_CURRENT_USER, payload: username } );
dispatch( { type: ACTION_TWO, payload: username } );
dispatch( { type: ACTION_THREE, payload: username } );
} else {
dispatch( { type: SOME_OTHER_ACTION, payload: 'whatever state update you like' } )
}
}
}
Redux thunk would be the ideal way to do it but you can still chain actions by returning promises as your payload.(If you do not want to use thunk)
export function setCurrentUser(username) {
return {
type: SET_CURRENT_USER,
payload: new Promise(resolve => resolve(username))
}
}
export function actionTwo(username) {
return {
type: ACTION_TWO,
payload: new Promise(resolve => resolve(username))
}
}
export function actionThree(username) {
return {
type: ACTION_THREE,
payload: new Promise(resolve => resolve(username))
}
}
And then you can call all three like:
this.props.setCurrentUser(this.state.newUsername)
.then(newUsername => this.props.actionTwo(newUsername))
.then(newUsername => this.props.actionThree(newUsername))
I think you mapDispatchToProps function should look like this: const mapDispatchToProps = {
setCurrentUser: (data) => dispatch(setCurrentUser(data)),
}. Regarding accessing store from your action creators, you need to export it from where you declared it(index.js). import it to your actionCreators file and then use it there. It goes as follows:
in index.js file: export const store = createStore(...) and in your actionCreators file: import { store } from "your index.js file's path" and you are good to go. Hope that solves your problem.

Lift redux-token-auth Functions Out of View to Action File?

Looking for guidance using redux-token-auth. Throwing this Type Error:
Error
All the creators' examples involve making the calls from the Component class like so:
// EXAMPLE: components/SignInScreen.jsx
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { signInUser } from '../redux-token-auth-config' // <-- note this is YOUR file, not the redux-token-auth NPM module
class SignInScreen extends Component {
constructor (props) { ... }
...
submitForm (e) {
e.preventDefault()
const { signInUser } = this.props
const {
email,
password,
} = this.state
signInUser({ email, password }) // <-<-<-<-<- here's the important part <-<-<-<-<-
.then(...)
.catch(...)
}
render () {
const { submitForm } = this
<div>
<form onSubmit={submitForm}>...</form>
</div>
}
}
export default connect(
null,
{ signInUser },
)(SignInScreen)
Is it feasible to move the calls up to an action file? In documentation, he mentions that
registerUser, signInUser, and signOutUser are Redux Thunk actions and
thus, when wired through mapDispatchToProps return Promises.
I'm digging through the source code, but I can't figure out what changes when the sign in dispatch is mapped via Redux rather than directly imported and called. If anyone is familiar with this extension, any ideas would be much appreciated!
This is my attempt that throws the error:
// /actions/auth.js
import { signInUser, signOutUser } from '../redux-token-auth-config'
export const Login = (email, password) => {
return (dispatch) => {
dispatch(LoginStart());
signInUser({ email, password })
.then((response) => dispatch(LoginSuccess(response.data.data)))
.catch((error) => dispatch(LoginError(error)));
};
};
export const LoginStart = () => ({
type: 'LOGIN::START'
});
export const LoginSuccess = (data) => ({
type: 'LOGIN::SUCCESS',
payload: data
});
export const LoginError = (error) => ({
type: 'LOGIN::ERROR',
payload: error
})
export const Logout = () => {
return (dispatch) => {
dispatch(SessionCleanup())
signOutUser()
.then((response) => console.log('Success'))
.catch((error) => console.log(error))
}
}
export const SessionCleanup = () => ({
type: 'LOGIN::SESSION_CLEANUP'
})
Assuming you are trying to call Login from a component I had the same issue and fixed it by doing the following:
export default connect(state => ({}), { signInUser })(FooBar);
When I called the action I passed along signInUser.
this.props.fooBarBaz(email, password, signInUser);
This allowed me to use signInUser outside of the component just as I did within it.
So in your case it should be as simple as keeping:
export default connect(
null,
{ signInUser },
)(SignInScreen)
and calling Login like:
Login(email, password, signInUser);

Getting dispatch function return value

I'm new to react. I tried to separate component and action function. but I cannot get return value from separate action function. Is it possible to return a value (e.g Object {}) from dispatch function
I put the brief code as below :
LoginComponent.js
class Login extends React.Component {
constructor(props){
super(props)
this.state = {
username : '',
password : ''
}
}
submit = (e) => {
/* console.logging "Some response"*/
console.log(this.props.doLogin(this.state))
}
render(){
return (
<form onSubmit={this.submit}>/* some login element */</form>
)
}
}
export default connect(null, {LoginAction})(Login);
LoginAction.js
export function doLogin(state){
return dispatch => {
return axios.post('login', state).then(res =>{
return "Some response";
})
}
}
but It doesn't return any value
Thankyou.
Contrary the the above answer, you actually can return whatever you want from a thunk. Redux-thunk will pass it through.
In your case, where your thunk is returning a Promise<string>, that means that in your component this.props.doLogin(this.state) will also evaluate to a Promise<string>.
So instead of trying to log the Promise, instead try switching that log code over to this.props.doLogin(this.state).then(result => console.log(result);
You can use callback function
this.props.doLogin((this.state),(result)=>{
console.log(result)
})
export function doLogin(state,callback){
return dispatch => {
return axios.post('login', state).then(res =>{
callback(res);
})
}
}
Returning the function is not an option when you are using redux-thunk. it will run the callback and dispatch whatever you pass as an action object.
So as you want to make the api call ans see whether it is a success or failure . You need to dispatch and action on success. save it in redux state. and access the data in your component
export function doLogin(state){
return dispatch => {
axios.post('login', state).then(res =>{
dispatch({
data: "Some response",
type: "API_SUCCESS"
})
})
.catch(err) {
dispatch({
data: err,
type: "API_FAILURE"
})
}
}
And then access those values in your component like this
mapStateToProps = (state) => ({
data: state.yourreducer.data,
})
define mapDispatchToProps if you need dispatcch binded functions
export default(mapStateToProps, mapDispatchToProps)(YourComponent)

Resources