Why does redux thunk return promise? - javascript

I am learning redux-thunk middleware as a beginner react developper, and I don't understand why dos the function (returned by redux-thunk) returns a promise (returned by fetch())
I tried not to return anything, and it worked, so why do we return it?
export function getCourses() {
return fetch(baseUrl)
.then(handleResponse)
.catch(handleError);
}
export function loadCourses() {
return function(dispatch) {
dispatch(beginApiCall());// dispatch some synchronous action
return courseApi
.getCourses().then(courses => {
dispatch(loadCourseSuccess(courses));
}).catch(error => {throw error;});
};
}
For the component named MyComponent dispatching loadCourses() action
function MyComponent(props){
.......
useEffect(() => {
loadCourses()
});
const mapDispatchToProps = {
loadCourses,
.....
}
}

I think i got the answer(from a colleague).
if you want to chain certain activities, your action would need to return a Promise.
it's just a good way to allow chaining activities after the result is returned!

Well, first of all the function returns some data because you asked it to return some sort of result return function(dispatch) {...}.
If you want to ignore the returned result just remove the return from return function(dispatch) {...}.
Secondly, the function returns a promise because of the way that you have written your call to API functions (wrapped inside promise and not returning callbacks upon function completion).
If you want to get the actual result of the API call you should use the Async / Await syntax.

With a plain basic Redux store, you can only do simple synchronous updates by dispatching an action. Middleware extends the store's abilities and let you write async logic that interacts with the store.
Thunks are the recommended middleware for basic Redux side effects logic, including complex synchronous logic that needs access to the store, and simple async logic like AJAX requests.https://github.com/gaearon/redux-thunk
The thunk middleware knows how to turn thunk async actions into actions, so you just have to have your simple_action() to be a thunk and the thunk middleware will do the job for you, if the middleware see a normal action, he will dispatch this action as normal action but if it's an async function it will turn your async action into normal action.
You can also see return promise from store after redux thunk dispatch

Related

How do I make two dispatches in redux run in order?

Suppose I have two dispatches I want to fire in order called
dispatch(action1())
dispatch(action2())
Action1 is an action creator created using
createAsyncThunk
method from redux/toolkit.
Therefore, it uses async... await in the process.
Action2 is a synchronous process.
I want to make
`dispatch(action2())` run only after `action1()` has been dispatched.
How can I achieve this?
I have tried doing
dispatch(action1(), dispatch(action2())
but I have found that dispatching inside of a reducer is an anti-pattern since reducers are pure.
See Unwrapping Result Actions
Thunks may return a value when dispatched. A common use case is to return a promise from the thunk, dispatch the thunk from a component, and then wait for the promise to resolve before doing additional work:
So you can do this:
dispatch(action1()).then(() => {
dispatch(action2());
})
A simple way might be to let action1 conditionally dispatch action2 where you need it:
const action1 = createAsyncThunk(
'actions/1',
async (args, thunkAPI) => {
const response = await someApi.fetchStuff(args.id);
if (args.condition) {
thunkAPI.dispatch(action2());
}
return response.data
}
);
This only works if you don't depend on the reducer having updated the state at that point (usually with this type of scenario it's about the asynchronous work having finished).
If you however also depend on the state update, try the dispatch(action1()).then(...) answer someone else gave.
Another option is to write a "classic" thunk (not using createAsyncThunk) that coordinates the sequence of action1 and action2.
I personally prefer the last option since bigger thunks can be composed nicely of many smaller thunks.
In case you're wondering, createAsyncThunk is not meant to dispatch another action when returning its result - which would have also solved your problem: https://github.com/reduxjs/redux-toolkit/issues/773

use of redux-thunk with redux-observable

I'm using redux-thunk to manage async stuff in my react application and I want to use redux-observable to manage complex async flow more easily (concat multiple Ajax calls for example). Is there a way to do so without modify what's already done?
Here's an example of what I mean:
const fetchSomeData = () => {
return (dispatch, getState) => {
doAnAjaxCall((err, response) => {
if (err) return dispatch({type: 'ERROR', err})
// do something
return dispatch({type: 'SUCCESS', response})
})
}
}
Is it possible to use fetchSomeData inside an epic?
Since redux-thunk is promise based redux-observable should allow that, am I missing something?
Yep! You totally can use them together. Just place the redux-thunk middleware before the redux-observable middleware.
applyMiddleware(thunkMiddleware, epicMiddleware)
https://stackblitz.com/edit/redux-observable-playground-8c7pd9?file=ping-pong.js
Redux applies middleware in the order they are provided as arguments, so in this case we want the thunk middleware to absorb any dispatched thunks so that the thunk functions themselves never reach redux-observable (only pure actions). But your epics can still dispatch thunks since the redux-observable middleware uses store.dispatch under the hood.

Idiomatic way to await background tasks with redux + redux thunk

In my application, we have a somewhat long-running "provisioning" task that asynchronously completes that we essentially have to use polling to detect that it has finished. So that a user isn't blocked from using the app while this takes place, we kick that off at the beginning of the user interaction flow. However, at the end of the interaction flow, the user takes an action that needs to await that provisioning task if it hasn't completed. Here's a sort of sequence diagram to illustrate.
[provisioning start][poll for completion....]
[user interactions] [blocked action....][post provisioning task]
The problem I'm having is figuring out an idiomatic way to do this properly in Redux. Here are some things I've considered doing:
Have a "requests" reducer, where I'd store the Promise for that long-running provisioning task, then when [blocked action] executes, it simply awaits the promise. The problem with this is that Redux specifically requests that all store state is serializable, so it seems that things like Promises are not welcome there.
Subscribe to the store in the [blocked action] action creator and look for data that signals the provisioning has completed. Unfortunately, redux-thunk, which I'm pretty fond of, doesn't contain a reference to the store, just its dispatch and getState methods.
Having my React component wait for the provisioning to complete before executing [blocked action]. I don't like this because it adds too much action sequencing logic awareness to my view layer.
Since none of these seem like great options, I'm currently going with:
Store the provisioning Promise in a module property and essentially do option #1 without getting the Promise from the store. I'm not wild about this, since it moves state outside of the redux loop, but it seems like the easiest option.
Is there a more idiomatic way to achieve this with redux-thunk? If there's another asynchronous middleware that makes this cleaner, I'm willing to consider, though I'm already quite invested in redux-thunk.
There are several redux-promise-middleware options that let you dispatch promise actions and have the middleware consume them and emit PENDING,RESOLVED|REJECTED actions as the promise state changes. If you are working with promises and Redux, you might want to look at one of them. However they won't help you with this specific problem.
I think redux-saga is a middleware well suited to help you with this scenario. I've not yet used it personally so I can't provide an example.
Another, perhaps simpler, option is redux-tap. Use tap to expose the stream of actions that your app can subscribe to. Have a blocked action creator thunk subscribe to that stream and await the action that signals that the promise has completed (it should of course first check the contents of the store via getState to see if the promise completed before this blocked action was dispatched). Something like this:
// ========= configureStore.js
import ee from 'event-emitter';
// ...
export const actionStream = ee();
// ...
const emitActions = tap(({type} => type, (type, action) => actionStream.emit(type, action);
// install emitActions middleware to run *after* thunk (so that it does not see thunk actions but only primitive actions)
// =========== your action module.js
import {actionStream} from './configureStore';
export function blockedAction(arg) {
return (dispatch, getState) => {
if (getState().initDone) {
return dispatch({type: "blockedAction", payload: arg});
}
// wait for init action
actionStream.once("initAction", initAction => dispatch({type: "blockedAction", payload: arg}));
};
}
Keep in mind that it is not a binary "either thunk or xxx middleware" choice. You can load thunk as well as many other middlewares. In one app, I use:
thunk
a promise middleware
console logging middleware
custom middleware for batching updates
custom middleware for persisting actions to IndexedDB
custom middleware for pausing the action stream
middleware to prevent recursive store notifications
Without any middleware other than thunk, here is another option:
Option 5 - combine your option 2 with your option 4: Store the promise somewhere globally. Have the blocked action creator thunk await the promise before dispatching the raw action. To minimize the "state outside of the store", you can also have the promise action emit actions into the store to keep the store updated (possibly using a promise middleware)
I think this is simpler—unless I'm missing something—if we're more rigorous in thinking about the UI as a function of state. At every point in your interaction flow there are a few things that are either true or false:
Provisioning has started/not started
Provisioning has finished/not finished
User action is pending/is not pending
These three facts are enough to tell us what the user should be seeing on their screen at a given moment, so our state should include these three facts:
const initialState = {
provisioningStarted: false,
provisioningDone: false,
laterActionIsPending: false,
// ...
};
Using redux-thunk we can handle the provisioning action:
function startProvisioning() {
return dispatch => {
dispatch({ type: START_PROVISIONING });
pollProvisioning().then(postSetupInfo => {
dispatch({ type: DONE_PROVISIONING, postSetupInfo });
});
};
}
...and in our reducer:
function appReducer(state, action) {
// ...
switch (action.type) {
case START_PROVISIONING:
return { ...state, provisioningStarted: true };
case DONE_PROVISIONING:
return { ...state, provisioningDone: true, postSetupInfo: action.postSetupInfo };
case LATER_ACTION_PENDING:
return { ...state, laterActionIsPending: true };
// ...
}
}
As I said, this should be enough to tell us what the user should be seeing:
const Provisioner = ({provisioningStarted, provisioningDone, /* ... */}) => {
if (provisioningStarted) {
if (laterActionIsPending) {
if (provisioningDone) {
return <p>Finished! Here's your info: {postSetupInfo}</p>;
}
return <p>Provisioning...</p>;
}
return <button type="button" onClick={doAction}>Do action</button>;
}
return <button type="button" onClick={startProvisioning}>Start provisioning</button>;
};
export default connect(mapStateToProps, mapDispatchToProps)(Provisioner);
Not exactly redux-thunk but functionally equivalent is using
redux-background. A package I created to make the process easier.
It goes something like this:
import { startJob } from 'redux-background';
import store from './store';
store.dispatch(startJob('provision', function(job, dispatch, getState) {
const { progress, data } = job;
return new Promise((resolve, reject) => {
// Do some async stuff
// Report progress
progress(10);
// Return value
return 10;
})
}, { data: '...' } );
On your state when it ends you should have something like this
{
background: {
provision: {
active: true,
running: false,
value: 10,
error: null,
/* and lot of more metadata */
....
},
}
}

async call with reducer not work

i'm trying to implementing a Redux action that retrieves some json data with an asynchronous call using React Native. I put the function fetchRestaurant() everywhere inside component but i still get this error:
Cannot read property 'type of undefined.
here's the code inside action/restaurant.js
export function setFetchedRestaurant(restaurants){
return Object.assign({ type: types.SET_FETCHED_RESTAURANT, restaurants:restaurants } );
}
export function fetchRestaurant(){
var restaurants;
fetch('https://appetizing.000webhostapp.com/connect.php').then((response) => response.text()).then((responseText) => { dispatch(setFetchedRestaurant(JSON.parse(responseText)));}).catch((error) => {console.warn(error);});
}
and this is my call to fetchRestaurant() function inside my component:
componentDidMount(){
this.props.fetchRestaurant();
}
You need to use middleware to handle the asynchronous action. One such middleware is Redux thunk.
Here is your action creator written as a Redux thunk:
export function fetchRestaurant() {
return (dispatch) => {
var restaurants
fetch('https://appetizing.000webhostapp.com/connect.php')
.then((response) => response.text())
.then((responseText) => {
dispatch(setFetchedRestaurant(JSON.parse(responseText)));
})
.catch((error) => {
console.warn(error);
});
}
}
redux-thunk
Remember for this to work you will to insert redux-thunk into your middleware chain when configuring your Redux store
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';
const store = createStore(
rootReducer,
applyMiddleware(thunk)
);
How does a Thunk Action creator work?
This single thunk action creator is an action creator that will be handled by our redux thunk middleware since it fits the signature associated with thunk action creators, that is it returns a function.
When store.dispatch is called our actions will go through the middleware chain before they reach the store. Redux Thunk is a piece of middleware that will see our action is a function and then give this function access to the stores dispatch and get state.
Here is the code inside Redux thunk that does this:
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
Okay so that is why our thunk action creator returns a function. because this function will be called by middleware and give us access to dispatch and get state meaning we can dispatch further actions at a later date.
Remember redux-thunk is not the only solution. if we wanted to dispatch promises instead of functions we could use redux-promise. However I would recommend starting with redux-thunk as this is the simplest solution.
redux-promise
An alternative method to redux thunk for handling your async Redux actions is redux-promise. Instead of action creators that return a function you can dispatch action creators that return a promise.
Here is an example:
function actionExample(payload){
return new Promise( (resolve, reject) => {
resolve(data);
});
}
You can of course combine redux-promise and redux-thunk middleware so that you can dispatch functions that when called dispatch promises

react, redux chain a next dispatch

I'm still beginning in react and redux and now I followed this tutorial and it uses this middleware for dispatch. I was wondering how I would do another dispatch after the first one (to chain it)? I have something like this now.
fetchData() {
const { dispatch } = this.props;
const action = PageActions.fetchPage({slug: this.props.params.slug});
dispatch(action);
}
and wondering if I can dispatch(action).then(...) but the return of dispatch is always undefined. Is that possible?
If you would like to use async actions inside of your action creators you need to use middleware. The recommended middleware is thunk.
There is a good post on Stack about it's usage and appropriate situations. Stack Overflow Async Actions
This will allow you to dispatch many actions from a single action. However if you are wanting to "chain" actions together you should simply dispatch the second action at the end of the first actions definition.
ie
function getBookings() {
return (
RequestHelper.fetchEntities(Routes.bookings.all, {}, queryParams)
.then(res => dispatch(getBookingsSuccess(res));
)
}
...
function getBookingsSuccess(data) {
dispatch(showSuccess());
}

Categories