Passing external data into components - javascript

In my react app, I am currently passing a list of stores by calling the API directly from the URL.
const getStore = async () => {
try {
const response = axios.get(
'http://localhost:3001/appointment-setup/storeList'
);
return response;
} catch (err) {
console.error(err);
return false;
}
};
I pass this function into my useEffect hook where I would set my get a list of stores using resp.data.stores:
const [storeLocations, setStoreLocations] = useState([]);
useEffect(() => {
async function getData(data) {
await service.stepLocation.init();
const resp = await getStore();
setStoreLocations(resp.data.stores);
}
setFlagRender(true);
return getData();
}, []);
This works, however, I noted in useEffect there is a call await service.stepLocation.init(). There is a file that already takes care of all the backend/data for the component.
const stepLocation = {
// removed code
// method to retrieve store list
retrieveStoreList: async function ()
let response = await axios.get(
constants.baseUrl + '/appointment-setup/storeList'
);
return response.data.stores;
,
// removed code
Since this data is available, I don't need the getStore function. However when I try to replace response.data.stores in useEffect with service.stepLocation.retrieveStoreList no data is returned. How do I correctly pass the data from this file in my useEffect hook?

I think your useEffect should be like follows as you want to save the stores in your state.
useEffect(() => {
const updateStoreLocations = async () => {
const storeLocations = await service.stepLocation.retrieveStoreList();
setStoreLocations(storeLocations);
}
updateStoreLocations();
}, [])

Related

Trying to console.log data within useEffect. Not logging any information

function UserAccounts() {
const [accounts, setAccounts] = useState();
useEffect(() => {
async function fetchAccounts() {
const res = await fetch(
'https://proton.api.atomicassets.io/atomicassets/v1/accounts'
);
const { accounts } = await res.json();
setAccounts(accounts);
console.log(accounts);
}
fetchAccounts();
}, []);
}
I'm trying to understand why console.log shows nothing in this example and what is the correct way to console.log the data that is being fetched from the api.
Well, you need to get the structure of the returned payload from the API correct. It does not have an accounts property.
The payload looks like this:
{
"success":true,
"data":[{"account":"joejerde","assets":"11933"},{"account":"protonpunks","assets":"9072"}],
"queryTime": 1646267075822
}
So you can rename the data property while destructuring. const { data: accountList } = await res.json();
function UserAccounts() {
const [accounts, setAccounts] = useState();
useEffect(() => {
async function fetchAccounts() {
const res = await fetch(
'https://proton.api.atomicassets.io/atomicassets/v1/accounts'
);
const { data: accountList } = await res.json();
setAccounts(accountList);
// logging both the state and the fetched value
console.log(accounts, accountList);
// accounts (state) will be undefined
// if the fetch was successful, accountList will be an array of accounts (as per the API payload)
}
fetchAccounts()
}, [])
return <div>
{JSON.stringify(accounts)}
</div>
}
Edit: using some other variable name while destructuring, confusing to use the same variable name as the state (accounts).
Working codesandbox
One thing I would change is working with try/catch surrounding async/await statements.
If your await statement fails it will never reach the console.log statement.
Unless you have another component handling those errors, I would use it in that way.
That is my suggestion:
function UserAccounts() {
const [accounts, setAccounts] = useState();
useEffect(() => {
try {
async function fetchAccounts() {
const res = await fetch(
'https://proton.api.atomicassets.io/atomicassets/v1/accounts'
);
const { accounts } = await res.json();
setAccounts(accounts);
console.log(accounts);
}
} catch (err) {
console.log(err)
// do something like throw your error
}
fetchAccounts();
}, []);
}
since state function runs asyncronousely . therefore when you use setAccounts it sets accounts variable in async way , so there is a preferred way of doing this thing is as below
problems i seen
1.fetch result should destructured with data instead of accounts variable
2.setAccounts function is running async way so it will not print result immedietly in next line
import { useEffect, useState } from "react";
export default function App() {
const [accounts, setAccounts] = useState();
async function fetchAccounts() {
const res = await fetch(
"https://proton.api.atomicassets.io/atomicassets/v1/accounts"
);
const { data } = await res.json();
setAccounts(data);
}
// on component mount / onload
useState(() => {
fetchAccounts();
}, []);
// on accounts state change
useEffect(() => {
console.log(accounts);
}, [accounts]);
return <div className="blankElement">hello world</div>;
}
check here sample

Function not getting called in useEffect()

I want these two functions to be called every time the component renders, but they are not being executed. And when I put the functions in the dependency array it results in an infinite loop. Any idea why they are not being called?
function PortfolioComponent() {
const [requestedAssets, setRequestedAssets] = useState([]);
const [assets, setAssets] = useState([]);
useEffect(() => {
async function calcValue() {
Promise.all(
requestedAssets.map(async function (asset) {
try {
const response = await axios.get(assetData(asset.AssetId));
let cp = response.data.market_data.current_price.eur;
let value = Number(cp) * Number(asset.Amount);
return { ...asset, value: value, price: cp };
} catch (error) {
console.log(error.response.data.error);
throw error;
}
})
)
.then((newAssetArray) => {
setAssets(newAssetArray);
console.log(newAssetArray);
console.log(assets);
})
.catch((error) => {
console.log(error);
});
}
async function getAssets() {
try {
const response = await axios.get("http://localhost:4200/assets");
// Do as you wish with response here
const assetResponse = response.data.rows;
setRequestedAssets(assetResponse);
console.log(requestedAssets);
} catch (error) {
console.log(error.response.data.error);
}
}
getAssets();
calcValue();
}, []);
Also some weird behaviour I just discovered...
For example, this line of code:
let cp = await response.data.market_data.current_price.eur;
When I remove the await keyword and save it in VS code, the data is retrieved as expected. However, when I refresh the browser the arrays are empty again. The same goes for when I add the await keyword again and save. The same thing happens.
This is what worked for me. So, instead of having a useState variable for requestedAssets, I created a variable inside the getAssets method instead. I'm not exactly sure why this works and not the other way. But, if anybody could explain, that would be great.
function PortfolioComponent() {
//const [requestedAssets, setRequestedAssets] = useState([]);
const [assets, setAssets] = useState([]);
useEffect(() => {
async function getAssets() {
const response = await axios.get("http://localhost:4200/assets");
const requestedAssets = response.data.rows;
console.log(requestedAssets);
Promise.all(
requestedAssets.map(async function (asset) {
try {
const response = await axios.get(assetData(asset.AssetId));
let cp = response.data.market_data.current_price.eur;
let value = Number(cp) * Number(asset.Amount);
return { ...asset, value: value, price: cp };
} catch (error) {
console.log(error.response.data.error);
throw error;
}
})
)
.then((newAssetArray) => {
setAssets(newAssetArray);
console.log(newAssetArray);
console.log(assets);
})
.catch((error) => {
console.log(error);
});
}
getAssets();
}, []);
The recommendation is to declare your functions inside the useEffect, see the official documentation. If you keep scrolling in the docs, they even have an example similar to yours, with an async function.
If, for some reason, you do need to have your function declared outside the useEffect, you can use a useCallback, which allows you to declare them in the dependency array. Something like this:
const getAssets = useCallback(async() => {
try {
const response = await axios.get("http://localhost:4200/assets");
// Do as you wish with response here
const assetResponse = response.data.rows;
setRequestedAssets(assetResponse);
console.log(requestedAssets);
} catch (error) {
console.log(error.response.data.error);
}
}, [requestedAssets])
useEffect(() => {
getAssets()
}, [getAssets])
You can also see the section Do I need to specify functions as effect dependencies or not? in this blog here for more information.
PS: This blog is from Dan Abramov, one of the creators of React, so reliable source ;)

How to get the return value of a async function that returns a promise

So I have a code like this
const getAllProduct = async () => {
let allProduct = "";
let config = {
method: "get",
url: db_base_url + "/products/",
headers: {
Authorization: "Bearer " + token.access.token,
"Content-Type": "application/json",
},
};
try {
let response = await axios(config);
allProduct = response.data.results;
} catch (error) {
console.log(error);
}
console.log(allProduct);
return allProduct;
};
The console.log(allProduct) do prints an array.
The function will be called on the render method of react by
return (<div> {getAllProduct()} </div>)
I've tried to do
return (<div> {console.log(getAllProduct())} </div>
But the console.log on rendering returns to be Promise Object instead of the results array.
How can I go around this?
async functions return a Promise which means their result is not immediately available.
What you need to do is either await the result of calling getAllProduct() function or chain a then() method call.
Looking at your code, i assume that you want to call getAllProduct() function after after your component is rendered. If that's the case, useEffect() hook is where you should call your function.
You could define and call your function inside the useEffect() hook and once the data is available, save that in the local state your component.
First define the local state of the component
const [products, setProducts] = useState([]);
Define and call the getAllProduct() function inside the useEffect() hook.
useEffect(() => {
const getAllProduct = async () => {
...
try {
let response = await axios(config);
allProduct = response.data.results;
// save the data in the state
setProducts(allProduct);
} catch (error) {
console.log(error);
}
};
// call your async function
getAllProduct();
}, []);
Finally, inside the JSX, .map() over the products array and render the products in whatever way you want to render in the DOM.
return (
<div>
{ products.map(prod => {
// return some JSX with the appropriate data
}) }
</div>
);
use
getAllProduct().then(res => console.log(res))
async function always return a promise you use await before call it getAllProduct()
const res = await getAllProduct();
console.log(res)
In my case daisy chaining .then didn't work. Possibly due to fact that I had a helper JS file that held all DB related functions and their data was utilized across various React components.
What did work was daisy chaining await within an async. I modified code where it works for same Component (like in your case). But we can take same logic , put async function in different JS file and use its response in some other component.
Disclaimer : I haven't tested below code as my case was different.
useEffect( () => {
var handleError = function (err) {
console.warn(err);
return new Response(JSON.stringify({
code: 400,
message: 'Error in axios query execution'
}));
};
const getAllProduct = async () => {
let allProduct = "";
...
const response = await ( axios(config).catch(handleError));
allProduct = await response;
return allProduct;
}
},[]);
// Then inside JSX return
getAllProduct().then( data => {
// Make use of data
});

Getting the returned value from a fetch call

I'm trying to call an endpoint in an api that returns a value, I have a dream to call this fetch function inside another function, then save it to a variable.
But this returns a promise or if I get it working the function calls other things before the returned value.
Is the only way to do a timeout?
Here is a code example
async function someFetch() {
const res = await fetch("someurl");
const data = res.json();
return data;
}
function useFetched(someInput1, someInput2) {
const fetchedData = someFetch(); // need this input before anything else is called
const some_var = fetchedData + someInput1 + someInput2;
return some_var;
}
I've also tried to make the second function async and called await in front of someFetch(), but this returns a promise.
You need need to await the result of someFetch
async function useFetched(someInput1, someInput2){
const fetchedData = await someFetch() // need this input before anything else is called
const some_var = fetchedData + someInput1 + someInput2
return some_var
}
You cant make async useFetch[hook]. You need to use a callback here. The rough idea, you can pass setter function to useFetch to set value. And then can use that value.
Sample:
async function fetchTodo() {
const res = await fetch("someurl");
const data = res.json();
return data;
}
function useFetched(id, callback) {
useEffect(() => {
fetchTodo(id).then(data => {
console.log(data);
callback(data);
});
}, [id, callback]);
}
Working sample:
import React, { useEffect, useState } from "react";
async function fetchTodo(id) {
const res = await fetch("https://jsonplaceholder.typicode.com/todos/" + id);
const data = res.json();
return data;
}
function useFetched(id, callback) {
useEffect(() => {
fetchTodo(id).then(data => {
console.log(data);
callback(data);
});
}, [id, callback]);
}
export default function App() {
const [data, setData] = useState({});
useFetched(1, setData);
if (!data) return <h1>Loading...</h1>;
return (
<div className="App">
<h1>{JSON.stringify(data)}</h1>
</div>
);
}
Sandbox: https://codesandbox.io/s/misty-smoke-ls2y4?file=/src/App.js:0-607

React state not updating after async await call

I am using React Hooks and the useEffect to get data from an API. I need to make 2 consecutive calls. If I console.log the response directly, it shows the correct response, but when I console.log the state, its empty and its not updating. What am I doing wrong?
const [describeFields, setDescribeFields] = useState([]);
useEffect(() => {
const fetchData = async () => {
await axios
.all([
axios.get("someUrl"),
axios.get("someotherUrl")
])
.then(
axios.spread((result1, result2) => {
console.log(result1.data.result.fields); //shows correct response
setDescribeFields(result1.data.result.fields);
console.log(describeFields); //shows empty array
})
);
};
fetchData();
}, []);
It will not be displayed just after that if you want to see latest changes just console log before return statement as setState is async your console.log run before then setState
const [describeFields, setDescribeFields] = useState([]);
useEffect(() => {
const fetchData = async () => {
await axios
.all([
axios.get("someUrl")
axios.get("someotherUrl")
])
.then(
axios.spread((result1, result2) => {
console.log(result1.data.result.fields); //shows correct response
setDescribeFields(result1.data.result.fields);
})
);
};
fetchData();
}, []);
console.log(describeFields); //Check here
return(
)
Note : [] array in useEffect means it will run only one time. If you want to update as long as variable changes then add it as dependency.
Here is working example : https://codesandbox.io/s/prod-thunder-et83o

Categories