Why do redux-thunks need to be bound to dispatch? - javascript

I think I am missing a fundamental piece of understanding here. I am aware that in order for an action to be created and thus set off the redux chain of events, the action creator must be invoked using dispatch.
However, when we have a redux-thunk which returns a function which will call dispatch on our action creator, why must the redux-thunk also be called with dispatch?
For example take the following redux-thunk:
function savePerson(person: Person) {
return async (dispatch: any) => {
delete person.cars;
let newPerson = await axios.post('/api/people/addPeron', person);
dispatch(addPersonSuccess(person));
}
}
Calling this savePerson function without dispatch does not set off the redux flow, and I don't understand why considering the function it returns calls our action creator with dispatch. Can anybody clarify what I seem to be missing here?

All redux middleware follows the same general layout:
const middleware => store => next => action => next(action);
Why must the redux-thunk also be called with dispatch?
Like you correctly pointed out in your first paragraph, for an action/thunk to be evaluated by the redux middleware chain, it must be dispatched by the calling code.
I think the misunderstanding comes in here:
"...when we have a redux-thunk which returns a function which will
call dispatch on our action creator...".
While it is correct that the returned function dispatches an action, it is only half the story. Technically, you are dispatching twice: first savePerson and later addPersonSuccess. The former being a thunk and the latter, most likely, being a plain action.
Now, let's consider the current redux-thunk source code:
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
...
export default thunk;
Once you dispatch savePerson, the middleware recognizes your action as a function. It then injects dispatch as the first argument to later allow dispatching of other actions. Up until now, dispatch has not been invoked on your addPersonSuccess action. Only after your asynchronous call to add a person will the dispatch be called on addPersonSuccess.
I like to think of this as passing the redux context along in the thunk (dispatch, getState, etc).
References
http://redux.js.org/docs/advanced/Middleware.html
https://github.com/gaearon/redux-thunk

Related

What are some common Redux Toolkit's CreateAsyncThunk use cases

Could somebody please explain to me what and how to use createAsyncThunk like I'm 9 years old? What is the action string for? Is it temporarily created for logic/path reasons and destroyed soon later? What can I do with the action strings/what do people usually use it for? I've been staring at the documentation forever and I am not able to understand it.
This is how somebody else used the code, if somebody could decipher this I would be so happy.
const action = createAsyncThunk('send/sendAction', async (form, thunkAPI) => {
const data = await axios.post('/api', object);
data.reduxRequestId = thunkAPI.requestId;
return data;
}
Official documentation: https://redux-toolkit.js.org/api/createAsyncThunk
Well you can divide Async Thunk functions and Reducer functions into a separate types.
They aren't exactly the same and you should notice that.
Reducer functions can't execute asyncronous code, they can execute code and update the state, but if you want to fetch or update something from a server, and then update the Redux state, you will not be able to achieve this solely by using reducer functions.
So, that's why we need Action creators (or AsyncThunk functions), which let us to execute asynchronous code and then update the actual state of Redux.
const action =
you define a constant which receives (in this case) another function which is createAsyncThunk which receives two arguments the first one the action type and the second one the payloadCreator callback
const action = createAsyncThunk('send/sendAction', async (form, thunkAPI) => {
so in this case action receives a predefined object (createAsyncThunk).
If you remember, to use a reducer function you usually need two parameters, one is actionType and the second one is the payload.
with the createAsyncThunk the first parameter it receives is the actionType which is 'send/sendAction' this is the actionType your reducer will receive, and the async part which receives two parameters is the payload generator.
const action = createAsyncThunk('send/sendAction', async (form, thunkAPI) => {
const data = await axios.post('/api', object);
data.reduxRequestId = thunkAPI.requestId;
return data;
}
The function createAsyncThunk as it is returns two parameters, one is the actionType and the second one is the Payload exactly those which you need to execute a reducer function.
now if you want to use your method should be something like this.
dispatch(action(formValuesFromLocalState, APIInstance));
the arguments or parameters you pass to that function in this case (formValuesFromLocalState and APIInstance) will pass to the async function so they will be something like this
const action = createAsyncThunk('send/sendAction', async (form = formValuesFromLocalState, thunkAPI = APIInstance)
with those parameters the way your method will execute or you may want to do it should be something like this.
const action = createAsyncThunk('send/sendAction', async (form, thunkAPI) => {
const object = /some specific way you want to morph the form values/
const data = await axios.post('/api', object);
data.reduxRequestId = thunkAPI.requestId;
return data; (this is the actual data which will be returned as a payload).
}
in the documentation they put a better example
They execute the function this way:
dispatch(fetchUserById(123))
and the actual function is:
const fetchUserById = createAsyncThunk(
'users/fetchByIdStatus',
async (userId, thunkAPI) => {
const response = await userAPI.fetchById(userId)
return response.data
}
)
so, the function only receives in this case userId, thunkAPI is not used.
'users/fetchByIdStatus'
is the actionType which will be dispatched.
const response = await userAPI.fetchById(userId)
return response.data
and the API call is an asynchronous code, and the return statement return response.data
is the actual payload.
so in the end, the dispatch function may look something like this:
dispatch({type:'users/fetchByIdStatus', payload:(whatever response.data has)});
hope this explanation is understandable, and excuse me for my bad english.

Thunks in sequence on render

I have an app that fetches some user info on render. So when the app first boots up it fetches the data with the use of getUserInformation() function. User doesn't need to manually log in, the app is inside the company's internal network.
export function getUserInformation() {
return function (dispatch) {
getUser()
.then((data) => {
dispatch(
{type: GET_USER_SUCCESS, response: data}
)
})
.catch((error) => {
dispatch(
{type: GET_USER_FAILURE, response: error}
)
})
}
}
Now I want to fetch the version of the app to be available in the whole app. But the API call can only be fired once the user is logged in (so getUser() was called successfully). Should I just add the
.then(getVersion())
in the getUserInformation() action?
It doesn't seem clean but I have no idea how can I approach it differently.
Action creator is a proper place to dispatch actions in sequence. The documentation covers this:
Using an async middleware like Redux Thunk certainly enables scenarios such as dispatching multiple distinct but related actions in a row, dispatching actions to represent progression of an AJAX request, dispatching actions conditionally based on state, or even dispatching an action and checking the updated state immediately afterwards.
In case user information and version actions need to be tested separately (they should be located in different modules) or be used separately, action creators can be combined. This requires to return promises to chain them. This also shows the limitation of redux-thunk:
function getUserInformation() {
return async (dispatch) => {
try {
dispatch(
{type: GET_USER_SUCCESS, response: await getUser()}
)
} catch (error) {
dispatch(
{type: GET_USER_FAILURE, response: error}
)
}
};
}
...
function getVersion() {
return async (dispatch) => {...};
}
...
function getInitialData() {
return async (dispatch, getState) => {
await getUserInformation()(dispatch);
// we need to use getState to check if there was an error
// because getUserInformation returns a fulfilled promise any way
await getVersion()(dispatch);
};
}
It would make sense be to re-throw an error from getUserInformation, but it would be bad in case it's used separately from getInitialData because this would result in unhandled rejection. The alternative is even worse, to check if there was an error with getState().
This scenario requires a more sophisticated middleware than redux-thunk which is dead simple - possibly a custom middleware that is based on it and is capable of handling rejections.

What is the sole benefit of redux-thunk?

I am relatively newer to redux. I have gone through a lot of article still i am not getting a clear picture whats the real benefit of using redux-thunk.
All I understood is it allows you to return a function instead of object from action-creators? But what's the benefit? I have created few small react projects without using redux-thunk.
Let's consider the below snippets. Both behaves the same.
It would be great help if someone can explain me or point me to the correct resources to get a better understanding.
With redux-thunk
export function fetchContacts(){
return function(dispatch){
axios
.get('/contacts')
.then( contacts => dispatch({ type: 'FETCH_CONTACTS', payload: contacts}))
}
}
Without redux-thunk
const client = axios.create({
baseURL: "http://localhost:3000",
headers: {
"Content-Type": "application/json"
}
})
const url = '/contacts';
export function fetchContacts(){
return {
type: 'FETCH_CONTACTS',
payload: client.get(url)
}
}
The purpose of Redux itself, is to hold our application state. One of the great features of Redux is that we can change our state in a well-defined pattern and it's a pattern that we repeat over and over in our applications.
We call an Action Creator, this produces an Action. The Action flows into our Middleware, then our Reducers which then flows into our application State then into React. Then we sit around and wait for the user to initiate some change inside of our application that repeats the process all over again.
This process works fine with any kind of synchronous change. By synchronous I mean that we call an Action Creator which immediately flows into an Action, Middleware and our Reducers.
The vast majority of web applications we build, however, need to fetch data from asynchronous channels. In other words, its more common to call an Action Creator that is fetching data from an API or some asynchronous action and only when that request resolves are we actually ready to create an Action.
Vanilla Redux is not setup to handle this out of the box.
So, how do we handle these asynchronous Action Creators?
This is where Redux-Thunk comes into play. The purpose of Redux-Thunk is to give us direct control over the Dispatch method. The Dispatch method is part of the ReduxStore that contains our application state.
The Dispatch method handles:
Middleware
Reducers
State
When we normally call an Action Creator and it returns an Action, the Action ends up being returned into this Dispatch method. You have been using the Dispatch method behind-the-scenes in vanilla Redux.
So in practice, say you have a file in actions/index.js:
import axios from 'axios';
export function fetchUsers() {
const request = axios.get('http://somejsondata.com/users');
}
Redux expects us to return an action, but we do not yet have any data. I have to wait for my request to resolve before I have any data to send across to my Dispatch method.
So, lets use Redux-Thunk where all the existing rules for action creators kind of go out the window. Redux expects for us to return an Action which is a plain JavaScript object.
Redux-Thunk enables one other return type, which is a plain JavaScript function.
import axios from 'axios';
export function fetchUsers() {
const request = axios.get('http://somejsondata.com/users');
return () => {
};
}
The first argument is going to be the dispatch method:
import axios from 'axios';
export function fetchUsers() {
const request = axios.get('http://somejsondata.com/users');
return (dispatch) => {
};
}
If we pass an Action into dispatch, it's going to be sent off to all our different reducers.
export function fetchUsers() {
const request = axios.get('http://somejsondata.com/users');
return (dispatch) => {
request.then(({data}) => {
dispatch({type: 'FETCH_PROFILES', payload: data})
});
};
}
This is saying, we are going to wait for request to resolve with some amount of data and only when it has will I dispatch an action. In this case, it is going to have type FETCH_PROFILES and payload of data.
redux-thunk allows you to execute asynchronous operations.
In your first example you are sending the actual data returned by your API endpoint to your reducer. And the action will only be sent after the server has returned the data.
In your second example, you are sending a promise to your reducer which doesn't work as you would have to resolve your promise inside the reducer, which breaks the principle upon which reducers should be pure functions.
redux-thunk allows you to delay your actions in order to make async calls before dispatching. lets say you are retrieving user settings. the common use-case is to dispatch a REQUEST_FOR_USER_SETTINGS_IN_PROGRESS action so you can show a loader in your app, then make the http request for the data and when you get a response, dispatch a SUCCESS or ERROR action to update UI. it would look something like this:
const requestToGetCoins = await() => {
return (dispatch) => {
dispatch(requestToGetUserSettingsInProgress());
try{
const users = async httpService.getUserSettings();
dispatch(requestToGetUserSettingsSuccess(users));
}
catch(e){
dispatch(requestToGetUserSettingsError(e));
}
};
};
just wanted to emphasise that there is a better way to handle async actions in redux than redux-thunk, using an adhoc middleware which handles async actions and reduces much of the boilerplate. I suggest you take a look at this:
https://medium.com/#sht_5/minimal-code-for-redux-async-actions-c47ea85f2141

In simple terms, what's the difference between a thunk and a Higher Order Function?

I understand that both are functions that return functions.
My experience so far with thunks have been using them to return functions as opposed to just action objects so that I can work with async requests in Redux.
A closure is an implementation of a High Order Function (HOF) in order to create a new scope for private variables...right? Other examples of HOFs include map, reduce and filter.
Is there any thing else that explicitly defines a difference between the two?
Thanks.
I understand that both are functions that return functions
Your understanding is slightly incorrect
Thunks can return a value of any type – not just a Function type
Higher-order functions can return a value of any type – not just a Function type
My experience so far with thunks have been using them to return functions as opposed to just action objects so that I can work with async requests in Redux.
Thunks (and higher-order functions, for that matter) are not intrinsically related to any particular library (React, Redux) or any particular control flow (sync, async). A thunk is just a nullary function – they have a variety of common uses cases, but most commonly are used to delay the evaluation of a specific computation.
A closure is an implementation of a High Order Function (HOF) in order to create a new scope for private variables...right? Other examples of HOFs include map, reduce and filter.
A closure is not necessarily an implementation of a higher-order function. The function keyword (and => arrow function syntax) does create a closure tho which does have a new (lexical) scope, yes.
Is there any thing else that explicitly defines a difference between the two?
Yes. How they are the same:
they are both functions
they can both return values of any type
How they are different:
thunks are nullary functions (they accept no arguments)
higher-order functions accept a function as an argument and/or return a function
Perhaps the most critical distinction:
A thunk can only be considered a higher-order function if it returns a function.
Thunks are functions wrap expressions in order to delay their evaluation.
This delay is achieved in Redux thunk a when an action is called it returns a function. This function that is returned can then be called at a later time.
Here is an example of a thunk action.
function incrementAsync() {
// the below function is called by Redux Thunk middleware below.
return dispatch => {
setTimeout(() => {
// Yay! Can invoke sync or async actions with `dispatch`
dispatch(increment());
}, 1000);
};
A higher order function is just a function that either returns a function or takes a function as one of its arguments. Because this function returns another function that takes dispatch as an argument this is an example of a higher order function.
The code from the redux thunk middleware resembles this
function createThunkMiddleware() {
return store => next => action => {
if (typeof action === 'function') {
// since action is a function it is a thunk action creator
// so call it with store methods
return action(store.dispatch, store.getState);
}
// action is not a function so redux thunk ignores it
return next(action);
};
}
As soon as our thunk action creator is called it sends the action function through the middleware chain. When it reaches our thunk middleware this action is recognised as a function and therefore called again with the dispatch and getState methods of the store.
Because of this second function call we are now in the scope of the returned function from our thunk action creator. This means that we can now execute asynchronous logic and still have access to the store's getState and dispatch methods. This is why our thunk action creator can be considered a thunk expression. By using a higher order function we can have access to, yet postpone the use of the store's dispatch or getState method to a future time. As seen below, the increment action is called after a one second delay.
function incrementAsync() {
// the below function is called by Redux Thunk middleware below.
return dispatch => {
setTimeout(() => {
// Yay! Can invoke sync or async actions with `dispatch`
dispatch(increment());
}, 1000);
};

Is there any way I can execute a JavaScript export statement with some delay?

I'm building a tool using React and Redux. I'm using whatwg-fetch to make a server side call and fetch some data. And I have a reducer, which will have its store created in a callback, after the data has been successfully fetched. Here is my code:
import {createStore} from 'redux';
import 'whatwg-fetch';
let notificationCardList = [],
initialState,
store;
const fetchData = () => {
fetch('http://localhost:6767/file/data.json')
.then((response) => response.json())
.then((responseData) => {
notificationCardList = Object.keys(responseData).map(key => responseData[key]);
})
.then(initializeReducer);
}
const initializeReducer = () => {
initialState = {
notifCardsToBeDisplayed: notificationCardList,
notifCardToBeDisplayed: null
};
function manipulateNotificationCards (state = initialState, action) {
switch (action.type) {
case 'OPEN_CARD':
return Object.assign(state, {
notifCardsToBeDisplayed: null,
notifCardToBeDisplayed: action.card,
notifCardsContainerPreviousState: action.notifCardsContainerCurrentState
});
case 'DISPLAY_CARD_LIST':
return Object.assign(state, {
notifCardsToBeDisplayed: action.cards,
notifCardToBeDisplayed: null
});
default:
return state;
}
}
store = createStore(manipulateNotificationCards);
}
fetchData();
export {store, notificationCardList};
But since the store is being created as a part of the callback, due to asynchronous behaviour, the export statement is probably getting executed before the createStore() statement, hence I'm effectively exporting an undefined 'store'. I thought about placing the export statement in the callback as well, but then again, export statements can only be there at the top level. I also tried using setTimeout(), but that didn't work either. Is there any way I can export 'store' after it's created?
The usual scheme for handling asynchronous initialization is to use a module constructor function that gets the desired exported value and that module constructor either returns a promise or takes a callback and the asynchronous exported value is only available via that mechanism. This is because the async operation takes an indeterminate amount of time so the only way a caller can reliably use it is to wait for it to be available via some sort of promise or callback.
A cached promise is a very clean way of doing this. The module initialization stores a promise that will resolve with the desired async value. A module method fetches the promise and the caller then uses .then() on the promise. If the module has not yet finished fetching the async value, then the promise will not call the .then() handler until the value is ready. If the value is already available, then the .then() handler will be called immediately. In either case, the caller just fetches the promise, adds a .then() handler and then acts on the value in the .then() callback.
Some examples of these methods here: How to async require in nodejs

Categories

Resources