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

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.

Related

How to use hooks within function in React Js

I have a file where I keep all my functions that make api calls, then I import these functions inside components to use where needed. The issue I'm facing is that when api call is made and it returns 'unauthorized user'(because token has expired) I need to redirect users to the login page.
// apiCalls.js file
export async function getData(){
let response = await fetch(url)
let res = response.json()
// here I need to add redirect to /login if res.status.code is 401 for example
}
I tried to create a custom hook (with useNavigate) to use inside the function, but app throws error saying that hooks can't be used inside function. I can check status of the request inside the component(after I import function from apiCalls.js) but it doesn't seem like a correct way of approaching this as I'll have to add check inside every component that may use this function. Any advise is greatly appreciated
EDITED: to add context, I need to redirect user from a function( not functional component), function is exported from apiCalls.js file.
There's a great way to use hooks inside of a function - make the function a hook! The caveat is that this function will also need to follow the rules of hooks; a big one being the one you've just discovered: you should only be calling it inside a component or other hooks. If you're getting an error because of eslint, you generally also have to (and should) prefix this function with use (like useGetData).
export function useGetData(){
const navigation = useNavigation();
const getData = async (url) => {
let response = await fetch(url)
let res = response.json()
if (/* isInvalidStatus */) {
// navigate to '/login'
}
// return your data
}
return { getData }
}
export function MyComponent() {
const { getData } = useGetData();
// call getData() from useEffect or a click handler
// return jsx;
}
Brief explanation in case it helps:.
First we'll rename the function to follow convention, but we'll also have to remove the async keyword (which we address later). We'll add the useNavigation hook.
export function useGetData() {
// or whatever navigator your router provides
const navigation = useNavigation();
}
The hook itself can't be async, but we can expose a function in the hook's return object:
const getData = async (url) => {
// would probably use const instead of let
const response = await fetch(url);
if (response.status === 401 || response.status === 403) {
navigate('/login');
return;
}
return response.json();
}
return { getData }
And now in the component you can grab getData from useGetData and use it however you want; the auth guard logic will be handled for us in the hook, no matter which component we use it in.
Hooks are JavaScript functions, but you need to follow two rules when using them.
Don’t call Hooks inside loops, conditions, or nested functions.
Don’t call Hooks from regular JavaScript functions.
So if you want to use hook inside a function, change that function into hook

Having promise response inside Vue template interpolation

I'm using Vue 2 and the composition API. My current component receives a prop from a parent and I render different data based on it, on the screen - the prop is called "result" and is of type Object, containing multiple info. I receive one "result" at a time, but multiple will be rendered - you can think of my "result" as a google search result - which means that the page will have multiple results.
The issue I have is that for one of the info inside "result", I need to call an asynchronous method and display its result, which is what I cannot accomplish.
Currently this is what I have:
<div>
{{ getBoardName(props.result.boardReference) }}
</div>
Method inside the script:
async function getBoardName(boardReference) {
var result = await entriesApi.getBoardName({
boardReference: boardReference,
});
return result;
}
It displays "[object Promise]", although if I console.log(result) right before returning it, it's just what I need, so it seems to me as if the interpolation doesn't actually wait for the promise result.
I've also tried using "then" inside the interpolation:
{{
getBoardName(props.result.boardReference).then((value) => {
return value;
})
}}
I was thinking about using a computed property but I am not sure how that would work, as the response I need from the method has to be connected to each result individually.
Please ask for further clarifications if needed.
As you thought, the interpolation does not actually wait for the result so this is why you have a Promise object.
Since you are using the composition API, what you can actually do is to use a reactive state (using the ref() function if you are waiting for a primitive type, which seems to be the case here, or the reactive() function for objects) and update the state within the onMounted() lifecycle hook.
setup(props, context) {
const boardGameName = ref("");
onMounted(async () => {
boardGameName.value = await getBoardName(props.result.boardReference);
});
async function getBoardName(boardReference) {
var result = await entriesApi.getBoardName({
boardReference: boardReference,
});
return result;
}
return {
boardGameName,
};
}
Since you are dealing with async data, you could add a loading state so you can show a spinner or something else until the data is available.
If your board reference changes over time, you could use a watcher to update your board game name.
Good luck with your project!

RTKQ - Select data without hooks

I'm trying to select cached data from RTKQ without using the auto-generated query hook, but I'm having trouble understanding the docs
const result = api.endpoints.getPosts.select()(state)
const { data, status, error } = result
This is how the docs describe how to access the data, but I can't find any references on how to inject the state object "select()(state)".
I can't figure out how to access the data if I only call the select?
api.endpoints.getPosts.select()
Can someone explain me the difference between "select()" and "select()(state)"
Or what is the optimal solution to access the cached data from RTKQ?
The result of api.endpoints.getPosts.select() is a selector function for the result of using the "getPosts" endpoint without arguments.
Similarly, result of api.endpoints.getPosts.select({ page: 5 }) is a selector function for the result of using the "getPosts" endpoint the argument { page: 5 }.
A selector function is then called as selector(state) or passed into useSelector(selector).
If you write that altogether, you end up with api.endpoints.getPosts.select()(state).
#phry
Thank you for your answer! I'm not 100% sure I understood your answer. But it pointed me in a direction that enabled me to get the data.
I ended up creating a selector like the docs.
export const selectUser = (state) => userApi.endpoints.getUser.select()(state);
and in my function, I referenced it with getting the exported store from configureStore() method
const { data } = selectUser(store.getState());
But I'm not sure if this is the intended way to do it.

Not setting state in time

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.

Is it possible to use async await with regular function which is not marked async in react?

Please note, below code is not actual code. There`s lot omitted for bravity.
I am using react with typescript and all my components are functional components.
I have following function which is like
export const filterData = (data) => {
// do lots of filtering on data, does not make any api calls.
// only do Array.filter, map etc to remove unwanted data
};
This function is defined in separate utils file and imported in my react component. I am using it as following
//Fetch data using hook from API
const receivedDataFromAPI = useDetails(detailsParams);// Data is already received here. Verified that data is coming from network tab and console log
const cleanedData = allFilter(receivedDataFromAPI);
const allFilter = async (receivedData: IData) => {
const data = await filterData(receivedData);
// other code
}
Earlier I was not using it with async await but then, execution was not waiting for filterData to return result. After doing lots of trial and error, I came to async await and code is working fine now. The question is above pattern ok or I need to make filterData function async?

Categories

Resources