Not setting state in time - javascript

Hi people I'm still learning react and I have this function:
async function onSubmit(){
try{
const url = `https://maps.googleapis.com/maps/api/geocode/json?address=${value.label}&key=${key}`;
const response = await fetch(url);
const results = await response.text();
const data = await JSON.parse(results);
setCounty(data.results[0].address_components[4].long_name.split(" ")[0])
setAddress(data.results[0].formatted_address.split(",")[0]);
setTown(data.results[0].formatted_address.split(", ")[1]);
setZip(data.results[0].address_components[7].long_name);
console.log('this is my county ' + county);
upload()
}catch(err){console.log(err)}
}
Which isn't setting state in time for the next function call upload() which requires the states to be set since it makes another fetch using the states in the url. I did a little research and I though by using async await it would work but it's still not setting in time so I need help. How do I call upload() after the states have finished updating? Thank you for the help and if you have any feedback I'm very open to improving my code :)

Setting the state is happening asynchronously. You shouldn't expect variable county to be updated and presented in the given console.log().
What you can do is either creating a separate variable and log out that as the following:
const countyValue = data.results[0].address_components[4].long_name.split(" ")[0]
setCounty(countyValue)
// ... rest of the state updates
console.log('this is my county ' + countyValue)
Or using useEffect hook to capture changes on county state as:
useEffect(() => {
console.log('changed', county)
}, [county])
Be aware of using useEffect hook, you should add that code snippet into the root of your function component. It cannot be called within a function inside.
Suggested read is Using the Effect Hook.

Related

How can I update a state variable from a promise?

I am trying to determine if a customer has an active subscription or not. To do this I am utilizing the following code:
const stripe = require('stripe')('some-api-key');
export default function Example(){
// the user will automatically be considered non-subbed by default
const [isSubscriber, setIsSubscriber] = useState(false)
// grab the customer id from stripe
async function get_customer_id() {
const customers = await stripe.customers.search({
query: `metadata[\'some-meta-data-key\']:\'some-meta-data-value\'`
});
return customers.data[0]['id']
}
// grab the list of active subscriptions from stripe
async function customer_is_subscriber(){
const subs = await stripe.subscriptions.list({
status: 'active',
});
return subs
}
// determine if the customer id is in the list of active subscriptions.
// return true if so, false otherwise
async function test_equality(){
const customer_id = await get_customer_id();
const subbed = await customer_is_subscriber();
const answer = subbed.find(sub => sub.customer === customer_id)
return !!answer;
}
useEffect( () => {
async function load_result() {
const promise_function_return = await test_equality()
setIsSubscriber(promise_function_return)
}
load_result();
}, [isSubscriber]);
return (
// some react code
)
}
I have been able to successfully get all of my other functions where I am doing the comparisons for if a user is a subscriber but where I am having an issue is updating the state value (e.g. true if they are subbed, false otherwise).
I found some good past questions on this specific topic such as:
here The useState set method is not reflecting a change immediately
here: setState inside Promise in React
and here: setState inside a Promise function in a useEffect with hooks?
but I just have not been able to get it working correctly. This is currently the closest I have been able to get to solving this problem.
Currently your code says that, when isSubscriber changes, it should check if the user is a subscriber and update the isSubscriber state... so it's a chicken and egg problem. It won't set isSubscriber until isSubscriber gets set.
I think you want to change }, [isSubscriber]); to }, []); so that that code executes when the component first loads (not when isSubscriber changes).
The useEffect hook will always run on mount regardless of if there is anything in its dependency array. This means that your useEffect will work as is, and will run onMount as well as when isSubscriber changes:
useEffect( () => {
async function load_result() {
const promise_function_return = await test_equality()
setIsSubscriber(promise_function_return)
}
load_result();
}, [isSubscriber]);
To verify this, you can check out this codesandbox example. The useEffect looks just like yours, and you will notice that isSubscriber is initially set to false, but is updated to true after 3 seconds.
There's still an adjustment you may want to make even though that part appears to work ok. With isSubscriber in the dependency array, the function in your useEffect will be called any time isSubscriber changes. This probably not what you want, since this function doesn't actually depend on isSubscriber, but actually sets isSubscriber. In this case, that means test_equality() will be run on initial mount and then one more time after it sets isSubscriber, which is unnecessary.
This blog post explains the useEffect dependency array really well.
You can fix this by removing isSubscriber from the dependency array, like this:
useEffect(() => {
console.log("in useEffect");
async function load_result() {
const promise_function_return = await test_equality();
setIsSubscriber(promise_function_return);
}
load_result();
}, [isSubscriber]);
Since you mentioned the state value is not getting updated, there must be another issue going on in either get_customer_id() or customer_is_subscriber(). It would be good to double check and make sure the stripe api calls are working as expected.

Axios Promise result not copying to local function variable/local state

Requirement:
I require to make multiple API calls and then output the result onto the React Front-End.
I am using React hooks and not classes.
Explanation of steps taken:
In the view function (Function_V), I use a button to call a function (call it Function_A and it acts as the Service function), sending the parameters to that function. Function_A uses Axios to make a get call with the parameters and returns the required promise and the response content. I push parts of the returned output from Function_A onto a local array variable (in order to make a final response object). Taking few outputs from this Axios call, I make a function call to another function (Function_B) in Function_A's Axios call. This is another Axios call to get the rest of the required API response.
I successfully make the final response object in Function_A (utilizing both the outputs from Function_A and Function_B).
Finally, Function_A returns my custom response object as a promise.
Problems and things I tried:
I get the required response in the model function as a promise, but am unable to copy it to model function usual variables.
--> I tried making async and await (not good with the async-await and then-catch concept).
--> I created a local state in the model function and tried copying the response.
These are not working.
My issue is I cannot find a way to traverse the returned promise to access/store the response values in the local variables or set the local states with these values.
Below is the dummy code, please let me know if I am doing something wrong and how can I fix it.
Thank You All for taking out the time and helping me out! :)
Dummy Code:
View.js
import {Function_A} from ...;
function Function_V(){
e.preventDefault();
const [details, setDetails] = useState("");
async function search(e){
const r = await Function_A(param1,param2);
console.log(r);
setDetails(r);
}
return(
<div>
<form onSubmit={search}>
<button type="submit"> Get User Address Details</button>
</form>
{details.length>1 &&(
<div>{details}</div>
</div>
);
}
Service.js
import {Fucntion_B} from ...;
export async function Function_A(param1,param2){
let url=param1+param2;
let response={};
e.preventDefault();
let myAnswers=[];
let address;
const respon = await axios.get(url);
address=respon.address;
const w= await Function_B(address);
for(let s=0;s<w.length;s++){
myAnswer.push(w[s]);
}
respon={myAnswer,respon.address};
return respon;
}
Helper.js
export async function Function_B(param1_address){
let url=param1_address;
let count;
const r2= await axios.get(url);
count=r2.number;
return count;
}
The way you are using async-await is not correct. You are using it in a promise way which we should not. That is one of the reasons async-await was introduced to make users feel like synchronous calls.
export async myFun(){
const addrResponse = await axios.get(url);
const countResponse await axios.get(`${url}?param=${addrResponse.address}`)
const number = countResponse.number;
}
This is the ideal way, in case you want to create new array for setting state just use addrResponse or countResponse directly.
Solution:
The errors were arising due to the fact that Axios requests were taking place later that the rendering of the components, the response data was not getting passed onto the original function in object format.
Reason - ASYNC-AWAIT usage was done incorrectly. Upon correcting it will fix the error. The code in the question is fixed now.
Note:
Make sure to create a state in the view.js and set it to the response.
This way the result will be stored in the state and available for rendering.

Cannot read property '0' of undefined when I change back tabs in React

I am working on a personal project and I am trying to fetch data from an API that I have running on my server. Before the first implementation of the code, everything works fine, even after I write my code and save it the first time it works alright. But after I do this and switch my navigation tabs back and forth once, everything breaks and I get "TypeError: Cannot read property '0' of undefined"
I would like to fetch all my data in one place and then just get what I need from it willingly.
This is the fetching function:
//i have my state here
const [rezultateEz, setRezultateEz] = useState()
//code for fetching the data
const ezFetcher = async () => {
const intrebare = await fetch('http://localhost:5000/ezquestions')
const res = await intrebare.json();
setRezultateEz(res)
}
//i then call my function inside a useEffect
useEffect(() => {
ezFetcher();
}, [])
//and so i can just test it i try to run it inside this JSX here
<div className={style.questionHolder}>
{rezultateEz[0].question}
</div>
The res property has a question, all_answers array and correct_answer inside it, which I want to access more easily with this function but I can't really do it because of the error presented earlier. How can I do that?
I tried using try catch or saving different pieces of state individually but nothing worked.
I will kindly accept any piece of advice or information given, and thank you for the time you spent reading my question
All I had to do was to set
const [rezultateEz, setRezultateEz] = useState([])

Issues with asynchronous nature of redux in React?

I'm pulling data into one of my parent components and then using various filter statements which are based on user choices from select boxes. I'm then calling an action which simply stores that filtered data based on the users search into global state so that my child components can access them.
One of my child components is supposed to render the results but what is happening is the results being rendered are lagging one action behind. I've encountered similar issues when using set state and my solution then was to use a callback but I'm not exactly sure how to go about dealing with this issue in this situation with redux.
The wordpress.get is just named import of axios config.
componentDidMount = async () => {
const response = await wordpress.get(
"*********************/api/wp/v2/variants?per_page=100"
);
this.props.fetchData(response);
const data = []
response.data.forEach(ele => {
data.push(ele)
})
this.props.sendFilteredView(data);
};
handleChange = () => {
this.preBuiltFiltering();
};
I've left out pre-built filtering because its long and excessive, all it does is run the filter based on the users choices and then dispatches the this.props.sendFilteredView action with the filtered data set as the argument. The action just returns the payload.
I then am rendering the results of the filter in a child component by accessing the global state (I also tried just passing it directly through props, same issue).
It’s an async function, you’re using a callback after the forEach with data.
So you need to wait forEach been completed.
Try to use await before forEach.
componentDidMount = async () => {
const response = await wordpress.get(
"*********************/api/wp/v2/variants?per_page=100"
);
this.props.fetchData(response);
const data = []
await response.data.forEach(ele => {
data.push(ele)
})
this.props.sendFilteredView(data);
};
handleChange = () => {
this.preBuiltFiltering();
};

React await for data fetch with axios to happen before triggering next line is not working

here's a brief overview of my component OneRoadmap: in async componentDidMount, I am first calling "await this.props.getRoadmaps", which is an action in my Redux, this action will send a get request to my api which will retrieve items from my database, then, from those items retrieved, we send a dispatch to the Reducer, calling GET_ROADMAPS, this is done like
export const getRoadmaps = () => dispatch => {
dispatch(setRoadmapsLoading());
axios
.get("/api/roadmaps")
.then(res => dispatch({ type: GET_ROADMAPS, payload: res.data }));
};
and my component looks like:
async componentDidMount() {
await this.props.getRoadmaps();
var location1 = this.props.location.pathname;
var n = location1.slice(9);
var current_roadmaps = this.props.roadmap.roadmaps;
this.displayRoadmap = current_roadmaps.filter(
eachRoadmap => eachRoadmap._id == n
);
// now we have a roadmap
this.setState({
treeData: this.displayRoadmap[0].roadmap[0],
loading: false
});
}
GET_ROADMAPS will update my Redux state.
The problem appears to be: await this.props.getRoadmaps() will only wait until getRoadmaps() sends the dispatch to GET_ROADMAPS, which means it doesn't wait until GET_ROADMAPS update the redux state, this means few lines later, when I do this.props.roadmap.roadmaps, it is going to be undefined because this.props.roadmap.roadmaps is probably called before the GET_ROADMAPS finish updating my redux state.
Please guide me if there are any ways to solve to problem :) and please correct me if the problem is not what I think it is.
P.S. I referenced https://www.robinwieruch.de/react-fetching-data, this info on there will usually work but it appears to be that since I have an extra dispatch to my redux store, the dispatch updates the store after I call this.props.roadmap.roadmaps, which means I get undefined and can't use that variable in my rendering
It seems you’re not returning in your action creator. You have to explicitly return when you create a block (use curly braces).
Return your axios call and it should work properly.

Categories

Resources