Redux Action Creator and Dispatch confusion with syntax - reactjs

I'm having some trouble understanding this productListCreator syntax. Using thunk, the tutorial I'm following says that thunk allows us to write the an async function within a function.
productListCreator is a function that returns async function with dispatch as an argument.
But when you use call/use this with dispatch(productListCreator()), I'm confused.
productListCreator has no dispatch argument being passed. Instead it is being passed into the useDispatch hook.
So to me the code is saying useDispatch() which takes productListCreator function and runs it, which has no argument passed in for "dispatch".
import axios from 'axios'
const productListCreator = () => async(dispatch) => {
try {
dispatch({
type: 'PRODUCT_LIST_REQUEST'
})
const { data } = await axios.get('/api/products')
dispatch({
type: 'PRODUCT_LIST_SUCCESS',
payload: data
})
} catch (error) {
dispatch({
type: 'PRODUCT_LIST_ERROR',
payload: error.message
})
}
}
export default productListCreator
const dispatch = useDispatch()
// Retrieve all products at reload
useEffect(()=>{
dispatch(productListCreator())
},[])

productListCreator is a "thunk action creator". It defines the "thunk function", and returns it. That "thunk function" is what gets passed to dispatch. The thunk middleware then intercepts the thunk function, calls it, and passes in (dispatch, getState) as arguments.
I'd recommend reading through the new "Writing Logic with Thunks" docs page, which explains how thunks work.

Related

How does action creator which is a thunk receive a store's dispatch method?

I am learning redux and there is one thing puzzling me regarding the internal logic - how does a thunk receive dispatch as argument if a thunk is an argument to dispatch and not vice versa? Here is a sample code:
I am creating an action creator which is a thunk (it does not return an action itself but another function which eventually returns the action). I am defining it to receive dispatch function as argument, like this (code is simplified to serve as example):
export const fetchPosts = () => {
return async (dispatch) => {
const response = await fetch('some url');
dispatch({type: 'FETCH_POSTS', payload: response});
}
}
Then I use this thunk in App.js file, when I am getting a dispatch function from 'react-redux':
import { useDispatch } from 'react-redux';
import { fetchPosts } from './store/posts-actions';
function App() {
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchPosts());
},[dispatch]);
...
}
I am not passing dispatch as an argument to fetchPosts(). I am passing fetchPosts() to dispatch. And this is the part that I don't understand.
How does fetchPosts receive dispatch as argument if fetchPosts is an argument to dispatch and not vice versa?
This is covered in the Redux docs pages on Writing Logic with Thunks and Redux Fundamentals, Part 6: Async Logic and Data Fetching.
The Redux thunk middleware looks for any time that a function gets passed into dispatch, intercepts that function, and then calls it with (dispatch, getState) as arguments:
// standard middleware definition, with 3 nested functions:
// 1) Accepts `{dispatch, getState}`
// 2) Accepts `next`
// 3) Accepts `action`
const thunkMiddleware =
({ dispatch, getState }) =>
next =>
action => {
// If the "action" is actually a function instead...
if (typeof action === 'function') {
// then call the function and pass `dispatch` and `getState` as arguments
return action(dispatch, getState)
}
// Otherwise, it's a normal action - send it onwards
return next(action)
}

Redux action dispatch

In my redux action, I have one action will be called by another two actions, code is below:
export const addParticipantFromPopupRequest = (participant, project_id, currentStep) => async (dispatch) => {
const result = await addParticipant(participant)
dispatch({ type: PARTICIPANT_ADD, payload: result })
dispatch(updateProjectStep(project_id, currentStep))
}
export const handleFinalStep = (projectId, currentStep) => async (dispatch) => {
dispatch(updateProjectStep(projectId, currentStep))
}
const updateProjectStep = (projectId, currentStep) => async (dispatch, getState) => {
dispatch({ type: MODAL_STATUS_CHANGE, payload: { projectId, currentStep } })
dispatch({ type: PROJECT_PROCESS_LIST_UPDATE, payload: { project_id: projectId, currentStep } })
const { projectsProcessListsReducer } = getState()
localStorage.setItem("projectsProcessLists", JSON.stringify(projectsProcessListsReducer))
}
If I dont' use dispatch when call updateProjectStep, the addParticipantFromPopupRequest and handleFinalStep cannot run correct.
My question is can I call dispatch actions in this way and is it correct? Why I need the "dispatch" when I call updateProjectStep in another actions rather than call function name directly?
My question is can I call dispatch actions in this way and is it correct?
Yes. You should always call with the dispatch.
Why I need the "dispatch" when I call updateProjectStep in another actions rather than call function name directly?
If you call updateProjectStep directly without dispatch, it will become a normal js function call and your store won't be aware of it. Dispatch is the only way to trigger a state change in store.
In redux the store is single source of truth, the dispatch you are using is actually comes from store (store.dispatch).
If you call a function normally then it won't be aware by the store. That action won't pass through the middlewares (thunk/saga) that store is aware of and won't do the store update via reducers.
If store is not updated, your components won't receive any updates. Eventually your UI won't re-render.
You can find more about dispatch here.

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

How are getState and dispatch imported in redux-thunk action creator?

import _ from 'lodash';
import jsonPlaceholder from '../apis/jsonPlaceholder';
export const fetchPostsAndUsers = () => async (dispatch, getState) => {
await dispatch(fetchPosts());
_.chain(getState().posts)
.map('userId')
.uniq()
.forEach(id => dispatch(fetchUser(id)))
.value();
};
export const fetchPosts = () => async dispatch => {
const response = await jsonPlaceholder.get('/posts');
dispatch({ type: 'FETCH_POSTS', payload: response.data });
};
In the above code getState and dispatch functions are passed as arguments to the action creator function, what i m puzzled about is why are these functions not imported from anywhere or does react/redux somehow import them for us?
ok I will try to clear your confusion,
As you know action creators returns plain javascript object, but thunk is a middleware which allows you to return function instead of plain javascript object from the action creators, so when you use thunk if you return plain javascript object from action creator its handled in normal way, but when you return a function from action creator than thunk handle it and call this function with dispatch and getState, so you can dispatch an action asynchronously, you are not passing these arguments, see it this way that you are returning a callback from action creator and thunk call this callback with these arguments.
Hope it helps.
When you connect a react component with redux using a connect function provided by redux you will pass in to functions: mapStateToProps and mapDispatchToProps. Those will be the parameters your looking for (dispatch and getState).
Thunk is a function which optionally takes some parameters and returns another function, it takes dispatch and getState functions, and both of these are supplied by Redux Thunk middleware

Use getState to access key in redux state for API call

I'm a little new to using thunk getState I have been even trying to console.log the method and get nothing. In state I see that loginReducer has they key property which I need to make API calls. status(pin): true
key(pin): "Ls1d0QUIM-r6q1Nb1UsYvSzRoaOrABDdWojgZnDaQyM"
Here I have a service:
import axios from 'axios'
import {thunk, getState} from 'redux-thunk'
import MapConfig from '../components/map/map-config'
const origin = 'https://us.k.com/'
class KService {
getNorthAmericaTimes() {
return (dispatch, getState) => {
const key = getState().key
console.log('This is time key,', key)
if (key) {
dispatch(axios.get(`${origin}k51/api/datasets/k51_northamerica?key=${key}`))
}
}
// const url = `${origin}k51/api/datasets/k51_northamerica?key=${urlKey}`
// return axios.get(url)
}
}
export default new K51Service()
However in my corresponding action I get that Uncaught TypeError: _kService2.default.getNorthAmericaTimes(...).then is not a function
This is what the action function looks like :
export function getKNorthAmericaTime(dispatch) {
KService.getNorthAmericaTimes().then((response) => {
const northAmericaTimes = response.data[0]
dispatch({
type: ActionTypes.SET_NORTH_AMERICA_TIMES,
northAmericaTimes
})
})
}
I'm assuming it probably has to do with the if block not getting executed.
You should move your axios.get() method to your action creator and pass the promise to redux thunk, then when the promise is resolved dispatch the action with the response data so it can be processed by the reducer into the app's state.
actions
import axios from "axios";
export function fetchData() {
return (dispatch, getState) => {
const key = getState().key;
const request = axios.get();// use your request code here
request.then(({ response}) => {
const northAmericaTimes = response.data[0]
dispatch({ type: ActionTypes.SET_NORTH_AMERICA_TIMES, payload: northAmericaTimes});
});
};
}
Here's a very simple example of using axios with redux-thunk:
https://codesandbox.io/s/z9P0mwny
EDIT
Sorry, I totally forgot that you need to go to the state before making the request.
As you can see go to the state in your function, get the key from it, make the request and when the promise is resolved, dispatch the action with the response data. I've updated the live sample so you can see it working.
Again sorry...

Resources