ApiFetch pass variable from function JS - reactjs

I make an api call to an endpoing and inside the apiFetch I get a value. I want to display this value from outside the function but it doesn't get updated. Any ideas why it does this?
save: () => {
var newValue="default";
apiFetch( { path:'/url', } ).then( res => {
newValue = (res[0].value);
// this shows the new value
console.log(newValue);
} );
//this shows "default"
return <p>{newValue}</p>
}

Your function is asynchronous, you can use async/await
save: async () => {
const response = await apiFetch( { path:'/url', } );
const newValue = response[0].value;
console.log(newValue);
return <p>{newValue}</p>
}
Then, inside your caller block:
const newValueParagraph = await yourobject.save();

It looks like you need to return your Promise. Right now you make the async request to the api, and while that's doing it's thing you return newValue which is still just 'default.
Try like this:
save: () => {
var newValue = "default";
return apiFetch({ path: '/url', }).then(res => {
newValue = (res[0].value);
// this shows the new value
console.log(newValue);
return <p>{newValue}</p>
});
}

Related

Return the fetch response from another file

I am trying to call a function that calls fetch to an API from a React component in a separate file and am not finding the correct solution to get the correct response back.
When I debug, the result returns before the updateAccount function has completed and the final result is never returned to my update function.
Inside the fetch, the API returns the correct response whether it is successful or has validation errors and those results are correctly assigned to result.success and result.errors but the result doesn't get returned from the function so that the caller can make use of those values.
Inside of my React component:
import { updateAccount } from '../services/requests';
...
const update = (account: EditAccountModel) => {
const result = updateAccount(account);
if(result.errors.length > 0) {
// will notify of errors
console.log(result.errors); // is an empty array instead of validation errors
} else {
// will notify of success
console.log(result.success); // is an empty string instead of success message
}
}
...
My request file
export const updateAccount = (account: EditAccountModel | undefined): EditAccountResponseModel => {
const result = new EditAccountResponseModel();
fetch(baseUrl, {
method: 'PUT',
body: JSON.stringify(account),
headers
})
.then(response => {
if (!response.ok) {
return Promise.reject(response);
}
result.success = `${account?.name} was updated successfully!`
})
.catch(error => {
if (typeof error.json === "function") {
error.json().then(jsonError => {
result.errors.push(jsonError);
}).catch(genericError => {
result.errors.push(genericError);
});
}
});
return result;
}
The result reassignment happens inside then catch but it won’t be affective in the way you expected. The guaranteed way to return correct result is via a callback() passed to your updateAccount() if you could afford it:
export const updateAccount = (
account: EditAccountModel | undefined,
callback: Function
): EditAccountResponseModel => {
const result = new EditAccountResponseModel();
fetch(baseUrl, {
method: 'PUT',
body: JSON.stringify(account),
headers
})
.then(response => {
if (!response.ok) {
return Promise.reject(response);
}
result.success = `${account?.name} was updated successfully!`
callback(result);
})
.catch(error => {
if (typeof error.json === "function") {
error.json().then(jsonError => {
result.errors.push(jsonError);
callback(result);
}).catch(genericError => {
result.errors.push(genericError);
callback(result);
});
}
});
}
And inside your React component:
const update = (account: EditAccountModel) => {
const handleResult = (res) => {
// your result callback code
// ...
};
updateAccount(account, handleResult);
// ...
}
Alternative way that keeps your current structure is to change your current updateAccount() to an async function, then return await fetch().
You need to wait for the response . I'll let read more about how Promise work in JavaScript.
I wouldn't code updateAccount the same way you did, especially where you use the variable result and update it inside the flow of the promise (you really don't need that). You're also using React so you can use the state to store and update the result of the update function. But let's fix your problem first:
export const updateAccount = async (account: EditAccountModel | undefined): EditAccountResponseModel => {
const result = new EditAccountResponseModel();
await fetch(baseUrl, {
method: 'PUT',
body: JSON.stringify(account),
headers
})
.then(response => {
if (!response.ok) {
return Promise.reject(response);
}
result.success = `${account?.name} was updated successfully!`
})
.catch(error => {
if (typeof error.json === "function") {
error.json().then(jsonError => {
result.errors.push(jsonError);
}).catch(genericError => {
result.errors.push(genericError);
});
}
});
return result;
}
First make your function updateAccount async then await the result of the promise.
Now the same thing for the function update:
const update = async (account: EditAccountModel) => {
const result = await updateAccount(account);
if(result.errors.length > 0) {
// will notify of errors
} else {
// will notify of success
}
}

Data cannot be assigned to state in interval function in useEffect

Im getting data from axios async function and trying to assign to state in same function. When I print the values on console, i see that temporary value is not null but state is always null. when i rerender the page, state is not being null.
const [Pickup, setPickUp] = useState([]);
async function GetOrders() {
const result = await axios(
`EXAMPLEURL`,
);
setOrders(result.data);
var temp = [];
result.data.allOrders.forEach(element => {
if (element.order_type === 'PickupOrders') {
temp.push(element);
}
});
console.log(temp);
if (Pickup !== temp) {
setPickUp(temp);
}
}
useEffect(() => {
GetOrders();
const interval = setInterval(() => {
GetOrders();
console.log(Pickup);
}, 1000 * 5);
return () => clearInterval(interval)
}, []);
On console:
How can i fix this problem?
I assume you want to make a get request. Your axios function need to be completed such as ;
await axios
.get("YOUR URL", {
headers: // if you need to add header,
})
.then((response) =>{
setOrders(reponse.data);
})
.catch((error) => {
result = { errorMessage: error.message };
console.error('There was an error!', error);
});
return result;
Not completely sure what you're trying to achieve, but you can't compare Pickup !== temp this will be false all the time, you're comparing object references. Js will return all the time those values aren't equal.
This function GetOrders return a promise you don't need to use interval, you can use GetOrders.then(lambdaFunctionHere -> ());

ReactJs Unable to setSate in componentDidMount from async function

I'm calling an async function (getData()) in componentDidMount, and I'm trying to use this.setState with result of that function.
componentDidMount() {
let newData = getData();
newPodData.then(function (result) {
console.log('result', result)
this.setState({result})
})
}
However, I'm having issues getting my state to properly update. Some additional context - I'm trying to set my initial state with data I am receiving from a database. Is my current approach correct? What's the best way to accomplish this? Here's my async function for more context:
const getTeamData = async () => {
const getTeamMembers = async () => {
let res = await teamMemberService.getTeamMembers().then(token => { return token });
return res;
}
const getActiveTeams = async () => {
let res = await teamService.getActiveTeams().then(token => { return token });
return res;
}
const teamMemberResult = await getTeamMembers()
const activeTeamsResult = await getActiveTeams();
// get team member data and add to teamMember object
let teamMemberData = teamMemberResult.reduce((acc, curr) => {
acc.teamMembers[curr.id] = curr;
return acc;
}, {
teamMembers: {}
});
// get team ids and add to teamOrder array
let activeTeamsData = activeTeamsResult.map(team => team.id)
let key = 'teamOrder'
let obj = []
obj[key] = activeTeamsData;
const newObject = Object.assign(teamMemberData, obj)
return newObject;
}
export default getTeamData;
Changing the function inside the then handler to an arrow function should fix it. e.g:
componentDidMount() {
let newData = getData();
newPodData.then((result) => {
console.log('result', result)
this.setState({result})
})
}
But I'll like to suggest a better way to write that.
async componentDidMount() {
let result = await getData();
this.setState({result})
}

ExtJS - How can I use async - await functions

I have the following code, using async - await... that works in HTML + JavaScript environment, except if I use it inside EXTJS App, component listener.
...
onListInitialize: function(component, eOpts)
{
const citiesRef= db.collection("cities");
// defining async function
async function getIsCapitalOrCountryIsItaly() {
const isCapital = citiesRef.where('capital', '==', true).get();
const isItalian = citiesRef.where('country', '==', 'Italy').get();
const [capitalQuerySnapshot, italianQuerySnapshot] = await Promise.all([
isCapital,
isItalian
]);
const capitalCitiesArray = capitalQuerySnapshot.docs;
const italianCitiesArray = italianQuerySnapshot.docs;
const citiesArray = capitalCitiesArray.concat(italianCitiesArray);
return citiesArray;
}
//We call the asychronous function
getIsCapitalOrCountryIsItaly().then(result => {
result.forEach(docSnapshot => {
console.log(docSnapshot.data());
});
});
}
...
I'm getting the error: Expected an assigment or function call and instead saw an expression.
I tried Ext.Promise without success.
SOLVED! Using one promise for each query.
Sample of code:
Ext.Promise.all([
new Ext.Promise(function(resolve, reject) {
setTimeout(function() {
resolve('one');
}, 5000);
}),
new Ext.Promise(function(resolve, reject) {
setTimeout(function() {
resolve('two');
}, 4000);
})
])
.then(function(results) {
console.log('first function result', results[0]);
console.log('second function result', results[1]);
Ext.Msg.alert('Success!', 'All promises returned!');
});

Clean up async function in an useEffect React hook

I have the following useEffect function and trying to find the best way to clean this up when the component unmounts.
I thought it would be best to follow the makeCancelable from the React docs, however, the code still executes when the promise is cancelled.
const makeCancelable = (promise) => {
let hasCanceled_ = false;
const wrappedPromise = new Promise((resolve, reject) => {
promise.then(
val => hasCanceled_ ? reject({isCanceled: true}) : resolve(val),
error => hasCanceled_ ? reject({isCanceled: true}) : reject(error)
);
});
return {
promise: wrappedPromise,
cancel() {
hasCanceled_ = true;
},
};
};
//example useEffect
useEffect(() => {
const getData = async () => {
const collectionRef_1 = await firestore.collection(...)
const collectionRef_2 = await firestore.collection(...)
if (collectionRef_1.exists) {
//update local state
//this still runs!
}
if (collectionRef_2.exists) {
//update local state
//and do does this!
}
}
const getDataPromise = makeCancelable(new Promise(getData))
getDataPromise.promise.then(() => setDataLoaded(true))
return () => getDataPromise.cancel()
}, [dataLoaded, firestore])
I have also tried const getDataPromise = makeCancelable(getData) without any luck. The code executes fine, just doesn't clean up correctly when the component unmounts.
Do I need to also cancel the two await functions?
In your makeCancelable function you are just checking the value of hasCanceled_ after the promise has finished (meaning getData has already executed entirely):
const makeCancelable = (promise) => {
let hasCanceled_ = false;
const wrappedPromise = new Promise((resolve, reject) => {
// AFTER PROMISE RESOLVES (see following '.then()'!), check if the
// react element has unmount (meaning the cancel function was called).
// If so, just reject it
promise.then(
val => hasCanceled_ ? reject({isCanceled: true}) : resolve(val),
error => hasCanceled_ ? reject({isCanceled: true}) : reject(error)
);
});
return {
promise: wrappedPromise,
cancel() {
hasCanceled_ = true;
},
};
};
Instead, in this case I would recomend you to go for a simpler and more classic solution and use a isMounted variable to create the logic you want:
useEffect(() => {
let isMounted = true
const getData = async () => {
const collectionRef_1 = await firestore.collection(...)
const collectionRef_2 = await firestore.collection(...)
if (collectionRef_1.exists && isMounted) {
// this should not run if not mounted
}
if (collectionRef_2.exists && isMounted) {
// this should not run if not mounted
}
}
getData().then(() => setDataLoaded(true))
return () => {
isMounted = false
}
}, [dataLoaded, firestore])

Resources