How does a react action have access to dispatch? - reactjs

In my react project I have an actions file that looks like this:
const startLoad = () => async (dispatch) => {
dispatch({
type: CONTENT_LOAD,
});
};
// Get all content
export const getContents = () => async (dispatch) => {
dispatch(startLoad());
// some more code...
};
So in this example, I know that dispatch is coming from the middleware and getContents has access to it because it was mapped using mapDispatchToProps. So I am assuming when getContents is called, its really called like this -> dispatch(getContents) but can this be replicated in plain JavaScript so I can see what is really going on? If I am wrong about why getContents has access to dispatch please let me know.
For example, how isstartLoad able to use dispatch just because dispatch called startLoad? I have noticed that it will also work if I call it like this: dispatch(startLoad(dispatch));.
Passing dispatch in to startLoad actually makes more sense to me so I don't get why that isn't required.
Edit: this is the closest example I could come up with on my own.
const fun = () => (fun2) => {
fun2();
}
const fun2 = () => {
console.log('hello');
}
fun()(fun2);

So I am assuming when getContents is called, its really called like
this -> dispatch(getContents) but can this be replicated in plain
JavaScript so I can see what is really going on? If I am wrong about
why getContents has access to dispatch please let me know.
redux-thunk has quite simple implementation
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => (next) => (action) => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
As you can see it checks if action is a function.
And bonus, if you like you can get the whole state and extra argument in params following dispatch
For example, how isstartLoad able to use dispatch just because
dispatch called startLoad? I have noticed that it will also work if I
call it like this: dispatch(startLoad(dispatch));.
It looks like middleware also handles above case.

Related

Purpose of Redux Thunk `([arg(s)]) => dispatch =>`?

The code below comes from a Udemy course on the MERN stack by Brad Traversy. I'm new to Redux and Redux Thunk and am trying to understand what the purpose of => dispatch => is. I know it comes from Redux Thunk, which was set up in the Redux store file. I think dispatch is being used here in order to dispatch more than one action from this function and read that the = ([arg(s)]) => dispatch => syntax is an example of currying (though that doesn't seem right since with currying each function has one argument).
I'd greatly appreciate any help understanding => dispatch =>.
(Other minor point of confusion: In the course the function setAlert is referred to as an action, but I'm not sure that's correct since it contains multiple dispatches of actions.)
export const setAlert = (msg, alertType, timeout = 5000) => dispatch => {
const id = uuidv4();
dispatch({
type: SET_ALERT,
payload: { msg, alertType, id }
});
setTimeout(() => dispatch({ type: REMOVE_ALERT, payload: id }), timeout);
};
There's a couple of things going on here:
1) setAlert is what is usually called an "action creator". It's a function that returns an action that you can dispatch later.
2) Redux-Thunk is allowing you to use functions of the form (dispatch) => {} as actions (in place of the more normal object form { type, payload })
It might help if you look at them individually before seeing how they combine together:
// This is an action creator (a function that returns an action)
// This doesn't use redux thunk, the action is just a simple object.
// Because it's an action creator you can pass in arguments
// Because it's not a thunk you can't control when then dispatch happens
const setAlertActionCreator = (msg, alertType) => {
const id = uuidv4();
return {
type: SET_ALERT,
payload: { msg, alertType, id }
};
};
// You use this as:
dispatch(setAlertActionCreator("msg", "type"));
// This is not an action creator it's just an action.
// This IS a redux-thunk action (it's a (dispatch) => {} function not a simple object)
// Because it's not an action creator you can't pass in arguments to get a custom action back
// Because it IS a redux-thunk action you can dispatch more actions later
const setAlertThunk = (dispatch) => {
setTimeout(() => dispatch({
type: SET_ALERT,
payload: {
message: "fixed message",
alertType: "fixed alertType",
id: "fixed id",
}
}), 5000);
};
// You use this as:
dispatch(setAlertThunk);
// When you combine both patterns you gain the ability
// to both pass in arguments and to create additional dispatches
// as the function runs (in this case dispatching again after a timeout.)
// I won't repeat your code, but will show how you would call it:
dispatch(setAlertActionCreator("msg", "type"));
// ie: you use it the same way as example 1.
The syntax () => dispatch => is equivalent to:
function response(){
return function (dispatch){
//Some code here
}
}
So, basically what you can say is, it is a modern way of writing the same function. => dispatch => is returning a function that will be executed when the action will be invoked.
Hope this will help.
=> dispatch =>
is nothing but a syntactical sugar over a function returning another function. Since Fat-arrow functions => are used in place of normal functions.
Like
function abc(a,b){
return a+b;
}
is same as const abc = (a,b) => a+b ;
So you dont have to write return keyword.
So in your case its the same => dispatch => and the one below , its returning an anonymous function with . dispatch as its arguement
function response(){
return (dispatch){
//some code here
}
}
Hope it helps, feel free for doubts

Multiple dispatch in componentDidMount

I want to make multiple request in componentdidmount right now this is what i am doing,
componentDidMount() {
const {
getVehicleGroup,
getDrivers,
getFuelTypes,
getUnassignedDevices,
getCarMake
} = this.props;
getVehicleGroup();
getDrivers();
getFuelTypes();
getUnassignedDevices();
getCarMake();
}
and saga is attached with all these actions. Now i want to run another function but only when these requests. This is something i am looking for
Promise.all([getVehicleGroup, getDrivers, getFuelTypes, getUnassignedDevices, getCarMake])
.then(anotherFunction)
Here is my mapDispatch
const mapDispatchToProps = (dispatch) => ({
getVehicleGroup: () => dispatch(vehicleGroupRequest()),
getDrivers: () => dispatch(driverRequest()),
getFuelTypes: () => dispatch(fuelTypeRequest()),
getCarMake: () => dispatch(carMakeRequest()),
getUnassignedDevices: () => dispatch(unassignedDevicesRequest()),
updateCurrentDriver: () => dispatch(change('addVehicleForm', 'FK_driver_id', [])),
onSubmitForm: () => dispatch(addVehicleRequest())
});
Although I mostly use redux thunk, I think it should be similar in your case.
Try to do the following in your mapDispatch function:
getVehicleGroup: () => async dispatch(vehicleGroupRequest())
Async functions wrap as promises and behave mostly the same, allowing you to do something like:
Promise.All([]).then(nextAction)
IMHO the two most "neat" ways are:
with pure sagas: you dispatch one more action to start a saga that take all the actions you want to wait before starting the anotherFunction (but, depending on what you want to do with anotherFunction it could be useless)
you can use redux-saga-thunk to get a promise from every saga you are starting and gain all the advantages of redux-thunk. I haven't used it directly but I'll do as soon as I need to do something similar

Redux getState in action not working

I'm getting a bit confused with getState() in redux. I am using the thunk middleware.
I have an auth action which is an async action. But I have an action which runs before which checks if a token exists in state and if its still valid.
My problem is i can't seem to check the state when I have called the action. Thought I could just use getState but that doesn't seem to be a function.
container.js
componentDidMount() {
this.props.authCheck()
}
...
function mapDispatchToProps(dispatch) {
return {
authCheck: () => checkApiStatus()(dispatch)
}
}
Action.js
export const checkApiStatus = () => (dispatch, getState) => {
const expires_at = getState().api.expires_at
if (!expires_at || isDateGreater(expires_at)) {
// dispatch async action
dispatch(apiAuth())
}
return
}
Anyone have any ideas. Or perhaps better way of implementing something like this?
Thanks
The problem is you explicitly calling the returned function in you mapDispatchToProps method and passing only one argument. Instead call dispatch(checkApiStatus()) then redux-thunk will take care of passing the right arguments to the returned method. Should look like this
function mapDispatchToProps(dispatch) {
return {
authCheck: () => dispatch(checkApiStatus())
}
}

Redux Thunk Sharing Async Object

If I have following thunk:
function postReq(body) {
return dispatch =>
superagent.post("/files")
.then(response => dispatch(actionCreator(response)));
}
How would I share the superagent request object with other parts of my code base? Would I pass it into the actionCreate and put it in the store?
I would like to abort the request on certain Events, that's the reason I am looking for this.
EDIT
To give more context to the Problem on hand. When a user uploads a file he has the option to abort the upload. As I am creating the superagent request within a thunk I need to pass the request object on to be able to call superagent.abort().
Well, first of all I would like to introduce you some ES6 features which would make your code WAY more readable. Right now you have:
function postReq(body) {
return dispatch =>
superagent.post("/files")
.then(response => dispatch(actionCreator(response)));
}
first you could use ES6 to make your function more readable in 2 steps:
Step 1
Update your action creator to be stored at a cost variable:
const postReq = (body) => {
return dispatch =>
superagent.post("/files")
.then(response => dispatch(actionCreator(response)));
}
Step2
Your function is returning a function so you can make it shorter and more readable with an implicit return:
const postReq = (body) => (dispatch) => {
superagent.post("/files")
.then(response => dispatch(actionCreator(response)));
}
Now, answering you could try to do what they expose here:
https://github.com/reactjs/redux/issues/1461#issuecomment-190165193
Which applied to your case would be something like:
const postReq = (body) => (dispatch) => {
superagent.post("/files")
.then(response => dispatch(actionCreator(response)));
const abort = superagent.abort.bind(superagent)
return { abort }
}
I have never done this myself but as far as I understand its binding the abort method to a variable which will be returned and executing the function stored there will call the abort method in the postReq context.

Redux - Wrap actions inside another action in mapDispatchToProps

I have several actions, that are need to be used with another one action,
more specific i have actions to work with doument and one action to save it, and i dont wont to repeat calling of save document, so i tried to create myself wrapper on the mapDispatchToProps and thunk.
I need to each action I pass to the method performed to thunk actions -
From
function changeAction() {
return: {
type: types.CHANGE
}
}
To
function change() {
return(dispatch, getState) => {
dispatch(changeAction())
saveDocument(getState());
}
}
I tried to create method to map actions to thunk
export function mapUpdatePresentationsToProps(actions) {
const mappedActions = {};
Object.keys(actions).map(action => {
mappedActions[action] = function() {
return (dispatch, getState) => {
dispatch(action)
dispatch(updatePresentation(getState()))
}
}
})
return mappedActions;
}
And I always get an error 'Actions must be plain objects. Use custom middleware for async actions.' Am i doing something wrong? I dont know how i can debug them. Will be grateful for any help.
I guess you need to change:
dispatch(action)
to something like:
dispatch(actions[action]())
Right now it seems that you try to dispatch a string

Resources