mapDispatchToProps to be explicit - reactjs

In react/redux I'm trying to convert this mapDispatchToProps to be explicit:
const mapDispatchToProps = { createOrganization }
I tried this:
const mapDispatchToProps = (dispatch) => {
return {
createOrganization: (organization) => {
dispatch(createOrganization(organization))
}
}
}
And this is the action
export const createOrganization = (organization) => ({
type: ACTION_CREATE_ORGANIZATION,
payload: api.createOrganization(organization),
})
But It's nor working. What can I do? Am I missing something?
The error is "Cannot read property 'then' of undefined". What it's happening is that once I enter a code, it should create an organization and redirect me to the page /dashboard, but it's not working
handleClick = (e, formData) => {
e.preventDefault()
if (formData.betaCode && formData.organization && this.props.userData) {
this.props.createOrganization({
name: formData.organization,
owner: {
id: this.props.userData.id,
name: this.props.userData.login,
ownerAvatar: this.props.userData.avatar_url
},
beta_code: formData.betaCode
})
.then(() => {
this.props.history.push('/dashboard')
})
}
}

Based on your code, createOrganization action should be an async. Something similar to:
const = createOrganization = organization => dispatch =>
api.createOrganization(organization)
.then(
response => dispatch({ type: ACTION_CREATE_ORGANIZATION, payload: response}),
error => dispatch({ type: ACTION_CREATE_ORGANIZATION_ERROR, payload: error}),
)
But it's not enough, you should install redux-thunk && redux-promise to handle such kind of action.
Rest of your code shouldn't be changed. Then you will be able to use mapDispatchToProps as you want:
const mapDispatchToProps = { createOrganization }
Hope it make sense. Async flow in redux

Related

React-TypeScript: Expected 0 arguments, but got 1

I'm not sure why I got this error. So basically I'm dispatching the id from Product component to getProduct redux action. Not sure why it isnt working.
// The product component
const Product: React.FC = () => {
const { id } = useParams();
const dispatch = useDispatch();
useEffect(() => {
dispatch(getProduct(id));
}, [dispatch, id]);
return (
<section className="product">
<div className="bd-container product-container"></div>
</section>
);
};
export default Product;
// getProduct redux action
interface productId {
id: string | undefined;
}
export const getProduct =
(id: productId) => async (dispatch: Dispatch<Actions>) => {
dispatch({
type: actionTypes.GET_PRODUCT_LOADING,
});
try {
const { data } = await axios.get(`${url}/api/products/${id}`);
dispatch({
type: actionTypes.GET_PRODUCT_SUCCESS,
payload: data,
});
} catch (error: any) {
dispatch({
type: actionTypes.GET_PRODUCT_FAIL,
payload: error,
});
}
};
Either an action object or action creator function could be passed to dispatch() function you got from useDispatch(). But here you are calling getProduct which is returning nothing, but dispatching actions.
Refer to more detail about useDispatch() hook here: https://react-redux.js.org/api/hooks#usedispatch
Redux has a dedicated article on how to deal with async logics here: https://redux.js.org/tutorials/fundamentals/part-6-async-logic

redux-logger not showing action names suddenly only "object, object"

I learn react and try to get Redux to work so I use the Redux-logger. When dispatching two actions from App.js it works as the top image show "ALBUME_DATA_LOADED".
Then I make a dispatch from from another place and get this output:
I'm not sure I sent that "object, object" action I place breakpoint and console log and it's strange the react-logger it catching an action that I dont think I sent..
Any idea?
Here is the action types I use in the below code as userActionTypes:
File user.types.js:
export const userActionTypes = {
SAVE_USER_START: 'SAVE_USER_START',
SAVE_USER_SUCCESS: 'SAVE_USER_SUCCESS',
SAVE_USER_FAILURE: 'SAVE_USER_FAILURE',
};
Here is the action:
File user.actions.js;
import { userActionTypes } from './user.types';
import { withFirebase } from '../../firebase';
import * as ROLES from '../../constants/roles';
const saveUserStart = () => ({
type: userActionTypes.SAVE_USER_START,
});
const saveUserSuccess = user => ({
type: userActionTypes.SAVE_USER_SUCCESS,
payload: user,
});
const saveUserFailure = errMsg => ({
type: userActionTypes.SAVE_USER_FAILURE,
payload: errMsg,
});
const asyncSaveUser = ({ firestore }) => {
return async dispatch => {
const userRef = firestore.userDoc(firestore.auth.currentUser.uid);
dispatch(saveUserStart());
firestore.db
.runTransaction(transaction => {
// This code may get re-run multiple times if there are conflicts.
return transaction.get(userRef).then(doc => {
if (!doc.exists) {
return Promise.reject('Transaction failed: User dont exist!');
}
const newRoles = doc.data().roles;
// new roll
newRoles.push(ROLES.USER);
// remove roll
newRoles.splice(newRoles.indexOf('ANONYMOUS'), 1);
// save it back
transaction.update(userRef, { roles: newRoles });
return newRoles;
});
})
.then(newRoles => {
dispatch(saveUserSuccess());
console.log(`Transaction successfully committed role(s): ${newRoles}`);
})
.catch(error => {
dispatch(saveUserFailure(error));
console.log(error);
});
};
};
export default withFirebase(asyncSaveUser);
in dispatch saveUserSuccess(), you can't pass newRoles.
dispatch(saveUserSuccess(newRoles));
The reason for this is your mapDispatchToProps.
const mapDispatchToProps = dispatch => ({
saveUser: () => dispatch(asyncSaveUser())
});
asyncSaveUser() is not an action creator.

Redux - how to combine multiple mapDispatchToProps?

I'm developing a react-native / redux app, it contains a few Reducer files that I'm able to combine using combineReducers(). In order to manage the code and keep it maintainable, these files also contain mapDispatchToProps entries (I've done this because dispatch functions are closely related to Reducers) something like:
export const counterMapDispatchToProps = dispatch => {
return {
increment: () => dispatch({ type: 'INCREMENT' }),
decrement: () => dispatch({ type: 'DECREMENT' }),
}
}
and
export const statusMapDispatchToProps = dispatch => {
return {
setStatus: (text) => dispatch({ type: 'SET_STATUS', status: text }),
}
}
If possible I'd like to take the outputs of these mapDispatchToProps and combine them so that I get a function that produces the following, which can then be used when connecting to my Redux store:
export const appMapDispatchToProps = dispatch => {
return {
increment: () => dispatch({ type: 'INCREMENT' }),
decrement: () => dispatch({ type: 'DECREMENT' }),
setStatus: (text) => dispatch({ type: 'SET_STATUS', status: text }),
}
}
I guess my question effectively comes down to: how do I write a combineMapDispatchToProps({counterMapDispatchToProps, statusMapDispatchToProps, ...}) function?
Since these functions just return an object (which is then handled by the Consumer HOC) you can use ES6 spreading -
export const appMapDispatchToProps = dispatch => {
return {
...counterMapDispatchToProps(dispatch),
...statusMapDispatchToProps(dispatch),
}
}
You can add more functions to this, and of course import them from other files.
If you need ES5, you can use Object.assign to similar effect:
export const appMapDispatchToProps = function(dispatch) {
return Object.assign(counterMapDispatchToProps(dispatch), statusMapDispatchToProps(dispatch));
}

How to wait for dispatch to complete using thunk middleware?

I am using redux-thunk and want like to dispatch an action and once that is finished make an api call with part of that updated store.
store.js
const middleware = composeEnhancers(applyMiddleware(promise(), thunk, logger()))
const localStore = loadStore()
const store = createStore(reducer, localStore, middleware)
graphActions.js:
First add an Element:
export function addElement(element) {
return dispatch => {
dispatch({
type: ADD_ELEMENT,
payload: element
})
}
}
Then make api call via different action creator:
export function saveElements() {
return (dispatch, getState) => {
let graphId = getState().elements.id
let elements = getState().elements.elements
axios.put(Config.config.url + '/graph/' + graphId, {
'data': JSON.stringify({elements: elements}),
}).then(() => {
dispatch({type: SHOW_SUCCESS_SNACKBAR})
}).catch((err) => {
dispatch({type: SHOW_ERROR_SNACKBAR})
dispatch({type: UPDATE_ELEMENTS_REJECTED, payload: err})
})
}
}
I need to make sure, that addElement() is finished before saveElements(), so that saveElements() accesses the updated store.
I tried the following:
export function addElement(element) {
const promise = (dispatch) => new Promise((resolve) => {
dispatch({
type: ADD_ELEMENT,
payload: element
})
resolve()
})
return dispatch => {
promise(dispatch).then(() => {
saveElements()
})
}
}
ADD_ELEMENT is dispatched, but the actions within saveElements() are not dispatched, no api call is made.
I was missing to dispatch saveElements() and returning dispatch(saveElements()).
export function addElement(element) {
const promise = (dispatch) => new Promise((resolve) => {
dispatch({
type: ADD_ELEMENT,
payload: element
})
resolve()
})
return (dispatch) => {
return addElements(dispatch).then(() => {
return dispatch(saveElements())
})
}
UPDATE:
Noticed I can simply do:
export function addElement(element)
return (dispatch) => {
dispatch({
type: ADD_ELEMENT,
payload: element
})
dispatch(saveElements())
})
}

Redux: Cannot read property 'then' of undefined

I'm trying to combine actions inside mapDispatchToProps. Trying to fetch data and after launch modal dialog. But I keep getting Cannot read property 'then' of undefined error.
Could somebody explain me, what am I doing wrong?
mapDispatchToProps:
const mapDispatchToProps = dispatch => ({
onClick: id => {
// console.log(fetchThing(id)) returns undefined
dispatch(fetchThing(id)).then(() => {
dispatch(showModal())
})
}
})
Redux action:
export const fetchThing = () => {
const request = axios.get(URL_API)
return dispatch => {
request.then(response => {
dispatch({ type: ACTION_FETCH, payload: response.data })
})
}
}
Why don't you use redux-thunk? With that you can write your action like that:
export const fetchThing = dispatch => axios.get(URL_API)
.then(response => dispatch({ type: ACTION_FETCH, payload: response.data }))
Redux thunk middleware will do all the stuff for you and you do not need to do that each time you need async actions within the mapDispatchToProps.
mapDispatchToProps should not have side effects. This should only do as the name suggests and hook up the actions to redux. This should be:
const mapDispatchToProps = {
fetchThing: yourAction.fetchThing,
showModal: yourAction.showModal,
};
export default connect(null, mapDispatchToProps)(YourComponent);
Then in your (probably) button:
<button type="button" onClick={this.handleClick} />
// this can be an asynchronous function, or you can just handle it in the action.
handleClick = async id => {
await this.props.fetchThing(id);
this.props.showModal();
};

Resources