How to create async/await structure for two redux actions? - javascript

I have two actions on button change function which are dependent on one another. What I want to do is I want to put these two function in async/await structure so that after update_other_filter action ends, that I will be able to run getTotalData action. Running it like below structure actually does not update state in correct way. I am sending previous state(before update_other_filter) in getTotaldata.
You guys will probably say I have to dispatch getTotalData inside update_other_filter action when it resolves. But in this state of my project it seems I can not change anything. I am not really good with async/await and promises concept so, I only want to create async/ await fucntion inside my react component than I want to call it inside onChange function. Is there a way to do that?
onChange = {(event) => {
this.props.setSpinner()
//this update filter function updates filter which will be sent to server in getTotalData action
this.props.update_other_filter(true,"website",!event.target.checked)
//this action should wait for update_other_filter to end than it has correct parameters to send to server
this.props.getTotalData(this.props.totalFilters, apiUrl)
}

async onChange = {(event) => {
this.props.setSpinner()
await this.props.update_other_filter(true,"website",!event.target.checked)
this.props.getTotalData(this.props.totalFilters, apiUrl)
}

// I will make function wait that needs for dependent function and also add some error handling.
async onChange = {(event) => {
this.props.setSpinner()
try
{
await this.props.update_other_filter(true,"website",!event.target.checked)
this.props.getTotalData(this.props.totalFilters, apiUrl)
}
catch(e)
{
thorw e;
}
}

Related

Problem with reading data from Cache API (still in promise?)

That is my first post here. I am not well skilled in asynchronous code so can not resolve the problem by myself
In a React/Redux app I have added cache. The idea behind it is to have something like 'Favorites' functionality but on clients' computer. So, I would like to store over there some data about books. While writing to cache works I can not successively dispatch data to store> Now my code looks like this:
export function fetchFromFavorites() {
return async (dispatch, getState) => {
const URL = getState().books.currentURL;
const Cache = await caches.open(URL);
Cache.matchAll()
.then(function (response) {
const ar = [];
response.forEach(async item => ar.push(await item.json()));
return ar;
})
.then(response => dispatch(test(response)));
};
}
In the code above test is an action that only sets the state field with payload. While the payload can be log-consoled from reducer, I can not perform on that any further action with another external function, well-checked on that kind of data. Besides DevTools mark it with blue 'i' what indicates that it has been calculated very lately. What is wrong with that code? BTW - it has nothing to do with service workers it is just inside regular React.
The function you are passing to response.forEach is returning a promise. You'd need to wait for all of those promises to resolve before returning ar.
For example, you may use something like:
// await all promises to be fulfilled before proceeding.
// note that we use response.map instead of forEach as
// we want to retain a reference to the promise returned
// by the callback.
// Additionally, we can just return the promise returned
// by `item.json()`
await Promise.all(response.map(item => item.json());
Remember, any function marked as async will return a promise wrapping the function's return type.
Note that you're mixing async/await with older style then/catch promises here. For consistency and ease of reading, you may want to use one style consistently.

How do I deal with multiple setState called in the wrong order?

Let’s imagine a simple component run where the user clicks on a button that shows a popup (setState(P)) and that causes both an HTTP (setState(H)) and WebSocket (setState(W)) server to respond with other data to be shown in the popup. Note that onClick, onHTTPResponse and onWSResponse are simple prop event handlers passed to the component which call setState:
onClick: --setState(P)-->
onHTTPResponse: ---setState(H)-->
onWSResponse: ----setState(W)-->
Now let’s imagine another run where WebSocket is faster than the HTTP response:
onClick: --setState(P)-->
onHTTPResponse: ----setState(H)-->
onWSResponse: ---setState(W)-->
Problem with this run is that the WebSocket setState(W) depends on the data returned by the HTTP response, hence it depends on setState(H) — setState(W) must occur after setState(H).
How would I deal with such situation?
Edit: it seems from the responses that I didn’t explain my problem clearly. onHTTPResponse and onWSResponse handlers are called with the response of each. This is the API I have available. I need to solve the setState call order from the usage perspective.
Note also that the idea of these prop handlers is that they’re called at unpredictable times. The same example could be made with subscribing to an event emitter for whichever data. So answers that involve async/await or promises don’t really make sense: I’m dealing with an event emitter kind of API.
Why don't you call them in the same order that you described?
If sending a message via WebSocket needs data from HTTP request you can wait for the request from HTTP and call send a message via Websocket afterwards.
Just an example;
import React, { useEffect, useState } from "react";
const exampleSocket = new WebSocket(
"wss://www.example.com/socketserver",
"protocolOne"
);
export default function App() {
const [httpData, setHttpData] = useState(null);
const [socketData, setSocketData] = useState(null);
const onClick = () => {
fetchData();
};
const fetchData = () => {
fetch("yourAPI")
.then(response => {
// Examine the text in the response
response.json().then(function(data) {
sendMessage(data);
setHttpData(data);
});
})
.catch(err => {
console.log("Fetch Error :-S", err);
setHttpData(err);
});
};
const sendMessage = data => {
exampleSocket.send(data);
};
useEffect(() => {
exampleSocket.onmessage = evt => {
// listen to data sent from the websocket server
const message = JSON.parse(evt.data);
setSocketData(message);
};
}, []);
return (
<div className="App">
<button type="submit" onClick={onClick}>
Send request
</button>
<div>{JSON.stringify(httpData)}</div>
<div>{JSON.stringify(socketData)}</div>
</div>
);
}
use setState callback
this.setState(p, () => this.setState(H, () => this.setState(W)))
setState callback will be triggered with updated state values, so no need to use async/await.
You can use aysnc await function with await in front of onHTTP function and onWebSocket function. Note that you need to put the code you want to run first above the one you want to run later. Example,
await onHTTP()
await onWebSocket()
They second one will only run when the first one is successful.
Since my question was rather unclear I decided to post a more generic one without using React (but pure JavaScript). It seems that using observables is a way to solve this problem. More details are posted in this other question: How to deal with race conditions in event listeners and shared state?

passing data with async/await in action function

I have the following two functions:
async function queryData(){
const query= await axios.get('...')
const queryStatus = portNames.map(...);
const dataStatus= await Promise.all(queryStatus);
return dataStatus;
}
export function actionData(){
const data = queryData();
return{
type:cst.RECEIVE_DATA,
payload:data
}
}
queryData() function return after some line code some data in promise...in the second function i put the data in payload for sending my action to reducer.
the problem is that when i'm trying to pass my data from first function in two second, if i output my variable in console.log() inside the second function,it shows:
instead if i try to print my variable inside the first function
i'm able to access my value from promise...what could be the problem that might create promise pending in actionData()?..therfore how can i pass my data value from promise to action in a way to dispatch my action with relative data to reducer?
Asynchronous functions always return promises. If you want to get access to the value they contain, you either need to call .then on the promise, or put your code in an async function and await the promise.
Since you are using redux, there are some additional considerations. By default, redux does everything synchronously. Dispatching an action should synchronously go through the reducers and update the state. To do async things with redux you'll need to add a middleware. There are a few possible async middlewares, but the one recommended by the redux team is redux-thunk.
With redux-thunk in your project, instead of dispatching an action object, you can dispatch a function. That function is empowered to do async things and dispatch actions when it's done. So a possible implementation for your case would be something like this:
function actionData() {
return async function(dispatch) {
const data = await queryData();
dispatch({
type: cst.RECEIVE_DATA,
payload: data
});
}
}
Let me explain to you the flow here.
First we come here
const data = queryData();
In queryData function we have:
async function queryData(){
const query= await axios.get('...') <------------ first breakpoint ----------------
// ......
}
When this async req is hit, the code doesn't stop it's flow, it continues. Next return statement is hit.
return {
type:cst.RECEIVE_DATA,
payload:data // data is undefined at this moment
}
In the meantime the async request's response comes back. We sequentially execute the next 3 statements of queryData() function
const queryStatus = portNames.map(...);
const dataStatus= await Promise.all(queryStatus);
return dataStatus;
And now the data variable gets updated.
To ensure proper flow, you could write something like this:
export async function actionData() {
const data = await queryData();
return {
type:cst.RECEIVE_DATA,
payload:data
}
}
I'd encourage to you read about async/await on MDN.

Is it synchronous to dispatch three actions inside a onClick functions and then map the data using that reponses

I want to parse one excel sheet and before parsing I want some data from backend to map it.
So after clicking on Submit button, I want to trigger three actions one by one and store the response inside store. I am using redux-saga for this.
After the three action (api calls), I will call the parsing function and do the parsing and mapping using that response I will be fetching from store.
I have tried dispatching the three actions one by one. But as soon as it reaches the network client i.e axios instance to call api it becomes async and the next line gets executed.
onSubmit = () => {
/* I will set the loader on submit button till the api is called and all parsing of excel sheet is done. */
this.setState({
showLoader: true,
}, () => {
this.props.getData1(); //Will be saving it in store as data1
this.props.getData2(); //Will be saving it in store as data2
this.props.getData3(); //Will be saving it in store as data3
/* After this I want to call the parsing function to parse the excel sheet data and map accordingly */
parseExcelData(sheetData); //sheet data is the excel data
}
So I expected that when I will call the 'parseExcelData' function, the data from store i.e data1, data2,and data3 will be available in that function.
But all the api call happens after the sheet is being parsed.
I have done it using saga generator functions and is working fine. But I want to know how to deal with this situation with redux.
Putting an api call (or any other async operation) into a saga does not make that action synchronous, it is still async. Separately, redux-saga really does not support getting a result from an action -- you trigger a saga with an action, so when the saga completes, it has no way to return a result to the code that originally triggered it. (You can try to work around this by passing a callback along with the action that triggers the saga, and have the saga call the callback, but I wouldn't recommend this approach.)
I would recommend implementing this without redux-saga, using traditional action creators. The action creators would return promises that make the async api calls, and resolve with the result when they're finished. That might look something like this:
// action creator getData1, getData2, getData3
export const getData1 = () => {
return fetch(apiUrl).then(result => {
return result.json();
}).then(resultJson => {
// also fire an action to put it in the store here
// if other parts of your app need the data
return resultJson;
}).catch(err => {
console.error(err);
});
};
// react component
// assumes 1, 2, and 3 cannot be parallelized
// could also be written with .then instead of await
onSubmit = async () => {
this.setState({showLoader: true}, () => {
const result1 = await this.props.getData1();
const result2 = await this.props.getData2(result1);
const result3 = await this.props.getData3(result2);
});
}
You could have the action creators dispatch an action to put the data in the store instead of resolving the promise with the result. But that means you have to pick up the new data via the component's props, which probably means something in componentDidUpdate that checks if the new props are different from the old props, and if so, calls the next data-fetcher. IMO that approach is much more awkward.

React - Setting state immediately after an async action

So I have a button. When you click on that button, it takes you to an onSubmit function which looks like this:
onSubmit(e) {
e.preventDefault();
this.props.nextSentence(); //this is async
this.setState({ //this is not yet updating with the right values then
inputField: this.props.activePupil.name
});
}
However: this.props.nextSentence(); is async, so when I set my state immediately after, there are no changes. Right now I have a second button which refers to a second function which just sets the state again. I would like to have this happen automatic though. How could I do this?
async actions are normally either Promises or functions with callbacks.
In case of a Promise you need to use .then like below
this.props.nextSentence().then(() => {
this.setState({...});
})
And in case of a function with callback
this.props.nextSentence(() => {
this.setState({...})
})
However keep in mind than you can get the returned response of your async action and use it to update your state. which is normally the case.
For example
//here response is a json object returned from server
this.props.nextSentence().then((response) => {
this.setState({
data: response.data
});
})

Categories