Set State erasing previous object value React - reactjs

I'm trying to make subsequent API calls to get infos about some financial indexes.. After each call, I want to update de indexObject so it has all the data from all the indexes I stored on the indexes array. The problem is, each time setIndexObject is called it overwrites the object, loosing the previous value even when i use the spread operator.
const [indexObject, setIndexObject] = useState({});
const indexes = [
"^GDAXI",
"^BVSP",
"^NDX",
"^DJI",
"^GSPC",
"^IBEX",
"^HSI",
"^BSESN",
"^FTSE",
];
useEffect(() => {
indexes.forEach((index) => {
const options = {
method: "GET",
url:
"https://apidojo-yahoo-finance-v1.p.rapidapi.com/stock/v2/get-summary",
params: { symbol: `${index}`, region: "US" },
headers: {
"x-rapidapi-key":
"----",
"x-rapidapi-host": "apidojo-yahoo-finance-v1.p.rapidapi.com",
},
};
axios
.request(options)
.then(function (response) {
setIndexObject({
...indexObject,
[index]: {
value: response.data.price.regularMarketPrice.fmt,
variation: response.data.price.regularMarketChangePercent.fmt,
},
});
})
.catch(function (error) {
console.error(error);
});
});
}, []);
This is what I am trying to achieve:
{
A:{
value:,
variation:,
},
B:{
value:,
variation,
}
}
Thanks for the help!

The problem here is you're looping the request, while the setState is not synchronous. It is better to populate all the data first, then set the state after that.
const [indexObject, setIndexObject] = useState({});
const handleFetchData = async () => {
const indexes = [
"^GDAXI",
"^BVSP",
"^NDX",
"^DJI",
"^GSPC",
"^IBEX",
"^HSI",
"^BSESN",
"^FTSE",
];
const resultData = {};
for(const index of index) {
const response = await axios.request(); // add your own request here
resultData[index] = response;
}
return resultData;
}
useEffect(() => {
handleFetchData().then(data => setIndexObject(data));
}, []);
why i use for of? because forEach or .map cannot await asynchronous loop.
Or if you want to fetch all the data simultaneously, you can consider using Promise.all

Related

React does not rerender on updated state of nested array

I have an array of objects like so:
const [categories, setCategories] = React.useState([
{
id: 1,
title: 'Top Picks',
subTitle: "Today's hottest stuff",
images: [],
searchQuery: 'shoes',
},
...]);
Which I update with values in useEffect once like so:
React.useEffect(() => {
const newCategories = categories.map(category => {
fetch(`https://api.pexels.com/v1/search?query=${category.searchQuery}&per_page=10`, {
headers: {
'Authorization': apiKey,
},
}).then(r => {
r.json().then(convertedJson => {
category.images = convertedJson.photos;
});
});
return category;
});
setCategories(newCategories);
}, []);
however the child components here never rerender and I cannot figure out why. My understanding is that .map creates a new array anyhow, so the spread syntax isn't necessary in setCategories() but regardless it does not work.
{categories.map((category, i) => (
<CategorySlider {...category} key={i}/>
))}
There's a few issues but the primary issue I see is you're returning the category before the fetch can complete - so even when those fetch calls inside your map complete, you already returned the category below before the fetch completes.
Try using the .finally() block:
React.useEffect(() => {
const newCategories = categories.map(category => {
const c = {...category}; // <--- good practice
fetch(`https://api.pexels.com/v1/search?query=${category.searchQuery}&per_page=10`, {
headers: {
'Authorization': apiKey,
},
}).then(r => {
r.json().then(convertedJson => {
category.images = convertedJson.photos;
});
}).catch((err) => {
console.error(err);
}).finally(() => {
return category;
});
});
setCategories(newCategories);
}, []);
Thanks! Using setState before the promises resolved was indeed the problem. The solution looks like this now:
React.useEffect(() => {
async function fetchImages() {
const promises = categories.map(async category => {
const response = await fetch(
`https://api.pexels.com/v1/search?query=${category.searchQuery}&per_page=10`,
{
headers: {
Authorization: apiKey,
},
}
);
const convertedJson = await response.json();
category.images = convertedJson.photos;
return category;
});
setCategories(await Promise.all(promises));
}
fetchImages();
}, []);

building dinamically array for react-simple-image-slider

React simple slider needs array of images at the star and some code inside html to initiate the slider
const images = [
{ url: "images/1.jpg" },
{ url: "images/2.jpg" },
{ url: "images/3.jpg" },
{ url: "images/4.jpg" },
{ url: "images/5.jpg" },
{ url: "images/6.jpg" },
{ url: "images/7.jpg" },
];
for images array I made
const [images, setImages] = useState([]);
maybe different way is better correct me pls.
Then I have useEffect where I fetch the data
useEffect(() => {
const getData= async () => {
try {
const response = await fetch(`url`, {
fetch request....
const ImagesArray = imagesArrayfromFetch.map((image) => ({
url: `/img/${image}`,
}));
console.log(ImagesArray);
setImages(ImagesArray);
console.log(images);
When I console.log ImagesArray - it gives me correctly filled array object.
console.log(images) gives me undefined. Here is an error probably
Then inside html I build the slider object
const App = () => {
return (
<div>
<SimpleImageSlider
width={896}
height={504}
images={images}
showBullets={true}
showNavs={true}
/>
</div>
);
}
So because setImages do not put array inside images slider object creates without images.
Is there a way to fix this?
It seems like you have a race condition.
Setting new state must happen after resolve.
Could be done in fetch().then( /* set state */ )
Cleaner way would be with await/async:
const fetchImages = async () => {
try {
const data = await fetch(...);
if (data) {
const ImagesArray = data.images.map((image) => ({
url: `/img/${image}`,
}));
console.log(ImagesArray);
setImages(ImagesArray);
}
} catch(error) {
console.error(error);
}
}
useEffect(() => fetchImages(), [])

Upload index to Algolia via Yahoo FInance API. Cannot access array even though I can console.log

The problem is that I can console.log the array from the useEffect hook, but when I go to access it, it returns an empty array.
I am trying to upload data to my Algolia index.
Here is the current useEffect:
useEffect(() => {
const fetchListings = () => {
api
.listNasdaq()
.then((response) => {
setListings(response.data);
console.log(response); //I can see the array of data here.
})
.catch((error) => {
console.log(error);
});
};
fetchListings();
}, []);
api.js:
listNasdaq: () =>
exchangeApi({
method: "GET",
url: "/companies/list-by-exchange",
transformResponse: [
function (data) {
// Do whatever you want to transform the data
console.log("Transforming Nasdaq Data...");
const json = JSON.parse(data);
const stocks = Object.keys(json["results"]);
const stockNames = stocks.map(
(stock) =>
(stock = {
stock,
stockName: String(json["results"][stock]["companyName"]),
symbol: String(json["results"][stock]["symbol"]),
industry: String(json["results"][stock]["industryOrCategory"]),
})
);
data = {
stockNames,
};
return data;
},
],
}),
Algolia:
nasdaqIndex
.saveObjects(listings, {
autoGenerateObjectIDIfNotExist: true,
})
.then(({ objectIDs }) => {
console.log("algolia stuff", objectIDs);
});
I'm a little confused by your map function. Are you injecting the full stock object back into itself ((stock = { stock, ...)? What does the final stock object look like when you log it? I'm worried that might be confusing Algolia when you go to save the record.
const stockNames = stocks.map(
(stock) =>
(stock = {
stock,
stockName: String(json["results"][stock]["companyName"]),
symbol: String(json["results"][stock]["symbol"]),
industry: String(json["results"][stock]["industryOrCategory"]),
})
);
Also, I assume the call to nasdaqIndex.saveObjects() occurs in setListings()? It looks like some of this code may be missing here.

How to get the data through the map and put it in the state

I wanted to use Axios get to put the contents value of 'card' and call the data and put the value in 'wordAll', but it failed.
I want to combine and store the arrays I received as an axios in the state through the map function.
Sometimes the code I wrote comes in and sometimes it doesn't. I know my code is wrong.
Please teach me the way.
const [wordAll, setWordAll] = useState([]);
useEffect(() => {
cards.map((contents) => {
axios
.get(`https/api/words/detail_list/?contents=${contents.contents}`, {
headers: {
Authorization: cookies.token,
},
})
.then((res) => {
let indata = res.data;
for (var i = 0; i < indata.length; i++) {
wordAll.push(indata[i]);
setWordAll(wordAll);
}
console.log('wordAll0', wordAll);
})
.catch((error) => {
console.log('err==>', error);
});
});
}, []);
console.log('wordAll1', wordAll);
You can keep the cards axios request promises in an array (cardsPromises) and then use Promise.all to get the values from the resolved promises.
useEffect(() => {
const cardsPromises = cards.map((contents) =>
axios.get(`https/api/words/detail_list/?contents=${contents.contents}`, {
headers: {
Authorization: cookies.token
}
})
);
Promise.all(cardsPromises)
.then((resp) => {
//resp will be an array of resolved values
setWordAll(resp);
})
.catch((error) => {
console.log("err==>", error);
});
}, []);

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 -> ());

Resources