Why do I need redux async thunks - reactjs

I'm new to whole react redux world but I would like to think that I now know how redux works. However right now I'm facing a new challange I need to implement async data fetching. I've choose to use axios as my http client.
The problem I have right now is that I've read about redux async thunk but I have no idea when and why would I use it. I understand that it adds middleware to redux which can handle async/await and promises.
I want to simply use axios to get data and then use dispatch to store them. Like this
const loadData = async () => {
const res = await axios.get('https://www.api.com/mydata');
const data = res.data;
dispatch(setMyData(data));
}
How would createAsyncThunk help me with that?

In your example, loadData only waits for one api request and then dispatches one simple redux action (setMyData). If you need it exactly like this, you're correct, why would you need thunks?
But imagine the following:
Several components in your app need to be aware that this api request is in progress (for example to show a loading indicator or to hide a button)
This function is needed in more than one place
You need to deal with specific error responses to the api request
Something in the global redux state could have changed while waiting for the api request to finish. You need to react to this before dispatching setMyData().
All of these are common requirements for complex react/redux apps, you might not have them right now but are likely to run into them at some point.
The thunk middleware provides an abstraction that deals with them. You could achieve the same by writing your own helper functions etc. but you'd reinvent the wheel in the end and a lot of react/redux devs would end up writing the exact same boilerplate code.

By design, redux actions are meant to be synchronous. Adding thunks like redux-thunk allow you to write action creators that are async and therefore return promises.
For example, I can write an action creator that looks like this:
const getUsers = () => async dispatch => {
let users = await getUsers();
dispatch({
type: GET_USERS,
payload: users
});
}
So instead of calling an api, fetching data, and then dispatching an action, you can have an action that does the fetching inside it

Related

How to wait for asynchronous operation to complete before rendering every time state changes

I'm working on an app that displays images that are dynamically generated by the backend in response to changes in the current state.
I only want a component to re-render after a state change after the asynchronous call to the backend has returned with the new image.
Then I want to atomically update the image (in a canvas, say) and other UI based on the state - e.g. some HTML.
If I was generating a new image synchronously on the client I could use a (Layout) Effect hook to update the canvas.
I could only change the state after the network request to the backend returns - but I'm not sure of the cleanest way of doing this. I have to copy a whole bunch of state (into a "pending" state) and then mutate a bit of it, send it to server and only apply it when the call returns? It could get a little complicated if there are multiple state changes in-flight at once. Then I need to continue to mutate "pending" state in response to subsequent actions.
I wonder - is there something in React (or Redux?) that can help with this? Or should I try code it up myself?
BTW I'm new to React.
You're basically looking for async actions you can learn to do it natively here or use a middleware like redux-thunk (or redux-saga but its a bit complicated to be honest).
The basics are for every async requests you need to make 3 actions
A action to start the request.
A action when the action completes.
A action if the request fails.
and the code will look something like
function async makeRequest() {
dispatch({type: 'START'})
try {
const res = await myRequest()
dispatch({type: 'SUCCESS', payload: res})
} catch (e) {
dispatch({type: 'FAILURE', payload: e})
}
}

React / Redux - async action is working without using thunk

I recently tried something in my project and I was quite surprised that it worked. What I did was this :-
let result = {};
Axios.post("/auth", newUser).then(res => {
// console.log(res);
result = res.data;
this.props.signupUser(result);
});
The above code was written inside onSubmit event handler of a form in react. I wanted to get the response from the POST request and store the response in redux store and I tried the above code and it worked. The result was stored in redux store. So my question is was this supposed to work? and if yes then what's the purpose of redux thunk?
signupUser is a simple action creator, a function which returns a plain object and the variable newUser is an object which contains the form data.
The purpose of thunk is to give you more control over the async actions. While your code works, consider some more advanced requirements:
A different React component is supposed to show a loading animation while the request is in progress
You you want to track all api error responses somewhere
after the user has signed up, you want to trigger other requests for unrelated resources
Thunk facilitates all of those things in a more convenient way. You can also look at the current redux state at any point with getState in thunk actions. In general, it's better to separate concerns: React components show a signup form and are wired up with redux actions. The actions take care of api requests and data handling. Nothing is stopping you from writing all that code inside of a React component, but in a larger app this will get out of hand quickly.

Dispatching to redux store from outside of a saga

I have a saga that initialize an analytics provider, which lives completely outside of the redux context. However periodically Redux will push an updated auth token to this analytics provider.
(Yes i know the analytics code should probably live in the saga flow, but trust me I considered it and that refactor is not possible right now)
function* setupAnalyticsProvider(response: any): any {
// get some global session data
setupAnalytics(data)
}
export function* refreshTokenIfNecessary() {
// syncs new JWT token with redux state
updateAnaltyicsProviderWithNewToke(token)
}
The problem is I want the analytics code to periodically request a token refresh. In order to do this I want to pass in a callback that lets the Analytics code dispatch an action to trigger the refreshTokenIfNesscary() saga.
function* setupAnalyticsProvider(response: any): any {
// get some global session data
setupAnalytics(data)
setAnaltyicsRefreshCallback(() => {
// Dispatch action
})
}
Is there anyway to hook into the store dispatch method, or maybe using Saga-Channels to achieve this?
For any periodic execution of your analytic code ,you would have to create a callback for it to flow ,if the refactor is the problem use Event channel sockets for emit ,on and yield your callbacks.

What is the difference between using redux-thunk and calling dispatch() directly

I'm in the learning phase of understanding redux state management and still trying to negotiate the bewildering jungle of boilerplate code and middleware, much of which I'm taking on faith as 'good medicine'. So I hope you'll bear with me on this perhaps rudimentary question.
I know that redux-thunk allows action creators to proceed asynchronously and dispatch a regular action at a subsequent time. For example, I can define a thunk action creator in my actions.js:
export function startTracking() {
return (dispatch => {
someAsyncFunction().then(result => dispatch({
type: types.SET_TRACKING,
location: result
}))
})
}
And invoke it from within a React component like so:
onPress={() => this.props.dispatch(actions.startTracking())}
My question is, what advantage does the above code confer over simply dispatching an action from inside an asynchronous callback?
import { store } from '../setupRedux'
...
export function startTracking() {
someAsyncFunction().then(result => {
store.dispatch({
type: types.SET_TRACKING,
location: result
})
})
}
which I would invoke inside my component
onPress={() => actions.startTracking()}
or even
onPress={actions.startTracking}
Is there anything problematic with accessing store directly via an import as I'm doing in the 2nd example?
There is nothing wrong doing so. From the redux-thunk page:
If you’re not sure whether you need it, you probably don’t.
The creator of redux explain the advantage of using it here:
This looks simpler but we don’t recommend this approach. The main reason we dislike it is because it forces store to be a singleton. This makes it very hard to implement server rendering. On the server, you will want each request to have its own store, so that different users get different preloaded data.
Basically, using redux-thunk will save you the store import in each action creator file and you will be able to have multiple store. This approach also give you the opportunity to write a little bit less code and to avoid spaghetti code. Many redux developer don't like to import the store and to manually dispatch because it can create circular dependencies if the code is badly separated (importing an action name from the action creator file in the reducers file and importing the store from the reducers file in the action creator file). Also, dispatching directly an action like that might break the middleware workflow, ie: the action might not be handled by a middleware.
But honestly, if you don't see an advantage to it yet, don't use it. If one day you have trouble with async actions, redux-thunk might be the answer.

Why use Redux Thunk [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
Why use Redux Thunk then one can do something like this:
ReadableAPI.getCategories().then((categories)=>{
console.log('after getCategories', categories)
this.props.dispatch(addCategories(categories))
})
Isn't this more straightforward and achieves the same thing?
This answer from Dan Abramov explains beautifully why you would want to use redux-thunk in your application. One more benefit of using redux-thunk in your application is that you keep your business logic separate from your view part (React in your case). We had a use case where our app was written in backbone and we wanted to re-write our whole app in React. We realised that it is easy if your view and collection/models are separate. We started depreciating just the html templates and not the collections. When we deprecated all the html templates, we started using Redux in our app and deprecated our collections too.
What I want to put here is that we had our view and business logic separate, we could refactor that easily. Similarly, with React and Redux, you would want to keep that different, so that if something new comes and replaces Redux, at least you won't have to deprecate your views and you just will have to change your business logic.
Redux Thunk basically allows us to delay the dispatch of an action, i.e we can handle the action returned by the action creator and call the dispatch function when we really want to dispatch.
Your example is incomplete and it was frustrating to follow how you arrived at that oversimplified solution. After researching it I realized, you probably have some ReadableAPI.js file somewhere you should have posted with what is probably a configuration using fetch and inside of it you probably have something like this:
export const getCategories = () =>
fetch('http://localhost:3001/categories', {headers})
.then(res => res.json())
.then(data => console.log(data))
which ties into your:
ReadableAPI.getCategories().then((categories)=>{
console.log('after getCategories', categories)
this.props.dispatch(addCategories(categories))
})
So in this solution you are returning a Promise which is an object which essentially gives us notification when some amount of work such as a network request is completed and in order to get notified we chain on the .then() function which we pass an arrow function like you did: then((categories)=> and that arrow function will be called at some point in the future.
It looks like you are referring to that data as categories and you are console logging 'after Categories', categories.
What we need to know is what are the different properties attached to that categories object? Does it have a data property? Does it have a results property with some actual data in it? Is there a categories.data.results that contains whatever the data is?
So let's just say the answer is yes to all the questions.
You are going about that in a bit of a hard way in order to deal with asynchronous requests because it's not just that snippet of code: there is also what's inside the ReadableAPI.js file, right? Also, you are using Promises that can get kind of hairy and you would have already put together two files just to deal with asynchronous request which would be okay if it was just a plain Reactjs application, but you mentioned your approach as an alternative to Redux-Thunk which implies using Redux.
For your approach in the vanilla Reactjs space I would use Axios and implement the async/await syntax, but with Redux involved you don't want to use a Promise.
Now, the action creator I had to make up in the ReadableAPI.js file would not work in a Redux environment because it does not return a plain JavaScript action object and so we would have to use a custom middleware as the error says to do.
So how does a middleware like Redux-Thunk work? Redux-Thunk essentially relaxes the rules around an action creator.
The purpose of Redux-Thunk is not to be passed a request object and it will take it away and go to work for you.
Redux-Thunk is an all purpose middleware that allows us to deal with asynchronous action creators, but it also allows us to do many other things as well.
With Redux Thunk involved, your action creator can return an action object. If you return an action object it still must have a type property and if it is an action object that gets returned it can optionally have a payload as well.
The other thing that Redux-Thunk does is allow you to return either an action object or a function.
If you return a function, Redux-Thunk will automatically call that function for you.
That's it, thats all Redux-Thunk does. However, One thing Redux-Thunk does really well is to manually dispatch an action. That is the key part. With Redux-Thunk we can manually dispatch an action at some point in time in the future.
So we get this new action created and it can be a plain JavaScript object or a function, but when we are dispatching it manually inside of Redux-Thunk or inside of a function it's basically always going to be a plain object.
So we will dispatch this action and it will flow back into dispatch and dispatch will send it right back into Redux-Thunk and Redux-Thunk will ask if it's an action or object.
When it's an object, Redux-Thunk forwards it automatically to all the different reducers.
With Redux-Thunk we can return a function and if we do, that function gets invoked with dispatch and getState arguments and with those two functions we have unlimited power over our Redux store and we can change any data and read any data and at any point in time in the future we can manually dispatch an action and update the data inside of our store.
Where am I getting the dispatch and getState? From the Redux-Thunk library source code:
https://github.com/reduxjs/redux-thunk/blob/master/src/index.js
src/index.js:
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
If you look at the if conditional you see the body of the actual logic that is going on. Did you just dispatch an action? If so, is it a function? If it is, then Redux-Thunk is going to invoke that action with dispatch and getState.
If our action is not a function, Redux-Thunk does not care about it, so on it goes to the next middleware as indicated by the return next(action);, otherwise on to the reducers if there is no middleware to run.

Resources