How to Store and Update then again store in useState - javascript

I'm using here useState to store data, update current value, by using this it's not store previous data, it's only store update current value which do useState. But I want to store previous data and update data all of them, somethings like, i have 10 api's data and i want to call them end of my api using ids.
i want to store 10 api's data in one array.
how to store 10 apis data in together using useState();
my code is somethings like:
const [dataLoc, setDataLoc] = useState([]);
const ids = [1,2,3,4,5,6,7,8,9,10]
useEffect(() => {
ids?.map((id) => {
fetch(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then((response) => response.json())
.then((dataLoc) => setDataLoc(dataLoc.title))
.catch((error) => console.error(error))
})
}, []);
console.log(dataLoc);
output is:
enter image description here
but it's in 10 array the out put but i want all the title in one array.
anyone can help me how can i do this here?
ThankYou for Your Trying in advance!

You can make use of react states prev-callback like that:
const [dataLoc, setDataLoc] = useState([]);
useEffect(() => {
ids?.map((id) => {
fetch(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then((response) => response.json())
.then((dataLoc) => setDataLoc((prev) => [...prev, dataLoc.title]))
.catch((error) => console.error(error));
});
}, []);
console.log(dataLoc);
By spreading ...prev and appending it with , dataLoc.title you add each entry to the previous state value. The order is the one in which the calls go through, since we insert the new element at the end.
Edit
If you want to console.log multiple times you can make use of another useEffect inside your component:
useEffect(() => {
console.log(dataLoc);
}, [dataLoc]);

Issue
The code is enqueueing state updates in a loop and not updating from the previous state. In other words, each enqueued update in the loop overwrites the previous update. The update from the last loop iteration is the one the app sees on the subsequent render.
Solution
Either use a functional state update to correctly enqueue state updates and update from the previous state. Use .forEach instead of .map.
const [dataLoc, setDataLoc] = useState([]);
useEffect(() => {
ids?.forEach((id) => {
fetch(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then((response) => response.json())
.then((dataLoc) => setDataLoc(dataLoc => [...dataLoc, dataLoc.title]))
.catch((error) => console.error(error))
})
}, []);
Or map the ids to an array of Promises and Promise.all them and enqueue a single state update.
const [dataLoc, setDataLoc] = useState([]);
useEffect(() => {
const requests = ids?.map((id) => {
return fetch(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then((response) => response.json())
.then((dataLoc) => return dataLoc.title)
.catch((error) => console.error(error))
});
Promise.all(requests)
.then(ids => {
setDataLoc(ids)
});
}, []);

In your case it is overriding the previous values. You can merge the values
React.useEffect(() => {
ids?.map((id) => {
fetch(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then((response) => response.json())
.then((dataLoc) =>
setDataLoc((prevState) => {
return [...prevState, dataLoc.title];
})
)
.catch((error) => console.error(error));
});
}, []);

you can use push method to add each title to the Array:
const [dataLoc, setDataLoc] = useState([]);
const ids = [1,2,3,4,5,6,7,8,9,10]
useEffect(() => {
ids?.map((id) => {
fetch(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then((response) => response.json())
.then((dataLoc) => setDataLoc(dataLoc.push(dataLoc.title)))
.catch((error) => console.error(error))
})
}, []);
console.log(dataLoc);

Related

Why getting too many rerenders in react?

I have the following code in React to get data from Firebase. I am new to useEffect and it is giving too many rerenders error:
let post1 = [];
useEffect(() => {
getDocs(collection(db, "posts")).then((snapshot) => {
const data = snapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
post1 = data;
})
}, [posts]);
setPosts(post1);
The way you wrote it, setPosts(post1) is being called on every render. I'm assuming posts and setPosts are destructured from a useState() value which means that every time you call setPosts(), it triggers a rerender. You need to move the setPosts() call to the useEffect(). You also need to remove posts from the dependency array of useEffect because if any of those dependencies change, it triggers a rerender as well. In your specific case, try this:
useEffect(() => {
getDocs(collection(db, "posts")).then((snapshot) => {
const data = snapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
setPosts(data);
})
}, []);

React .map not rendering

I've done a API call to get some datas then store it in an array and do a .map in the return
This is the code if you guys have any ideas it's been 2 hours that i'm stuck on this :(
import {useEffect, useState} from 'react';
import {useParams} from "react-router-dom";
import axios from "axios";
const CharacterScreen = () => {
const params = useParams()
const [character, setCharacter] = useState([]);
const [starships, setStarships] = useState([]);
useEffect(() => {
axios.get(`https://swapi.dev/api/people/?search=${params.character}`)
.then((r) => {
setCharacter(r.data.results[0])
getStarships(r.data.results[0])
})
.catch((e) => console.log(e))
const getStarships = (data) => {
let array = []
data.starships.forEach(element => {
axios.get(element)
.then((r) => {
array.push(r.data)
})
.catch((e) => console.log(e))
})
console.log(array)
setStarships(array)
}
}, []);
console.log(starships)
return (
<div>
<p>{character.name}</p>
<p>{character.eye_color}</p>
<p>{character.birth_year}</p>
<p>{character.gender}</p>
<p>{character.created}</p>
<p>{character.edited}</p>
{starships.map((element) => {
console.log('ok')
return (
<p key={element.key}>{element.name}</p>
)
})}
</div>
)
}
This is the .log of starships :
This is my return :
Any help would be apréciated
Use spread operator :
useEffect(() => {
axios.get(`https://swapi.dev/api/people/?search=${params.character}`)
.then((r) => {
setCharacter(r.data.results[0])
getStarships(r.data.results[0])
})
.catch((e) => console.log(e))
const getStarships = (data) => {
let array = []
data.starships.forEach(element => {
axios.get(element)
.then((r) => {
array.push(r.data)
})
.catch((e) => console.log(e))
})
setStarships([...array]) <=== //spread opeator
}
}, []);
The code inside your forEach will run asynchronously. You would have to wait for all that data to be actually populated in your array. async/await pattern + Promise.all(..) would be a good bet here and can be done like so :-
const getStarships = async (data) => {
let array = await Promise.all(data.starships.map(element => {
return axios.get(element)
.then((r) => {
return r.data
})
.catch((e) => console.log(e));
}))
setStarships(array);
}
Currently in your code by the time you do setStarships(array), array will be empty i.e. [].
Check this codesandbox where it's working :-
Note :- Don't pay attention to element.replace code, thats just for making secure requests
You have a syntax error, you should replace your bracket with a parenthesis like following:
{starship && starships.map((element) => (//here
console.log('ok')
return (
<p key={element.key}>{element.name}</p>
)
)//and here)}

API data not appearing in component but shows in console

I am making a rather simple API call to (https://api.punkapi.com/v2/beers) and displaying fetched data in the component, but somehow the data is not getting displayed on the page, but when I console log it the data is displayed correctly.
const Comp= () => {
const [item, setItem] = React.useState([]);
React.useEffect(() => {
fetch('https://api.punkapi.com/v2/beers')
.then((res) => res.json())
.then((data) => {
setItem(data);
})
.catch((err) => {
console.log(err);
});
}, []);
return (
<div>
{item.map((beer) => {
const { name, tagline } = beer;
<p>{name}</p>;
console.log(name);
})}
</div>
);
};
Issue
You don't return anything from your item mapping. Return the JSX you want to render (i.e. return <p>{name}</p>) and don't forget to add a React key to the mapped elements. Below I've simplified to an implicit return.
You should also remove the console log from the render return as "render" methods are to be pure functions without side-effects, such as console logging. Either log the fetch result in the promise chain or use an useEffect hook to log the updated state.
useEffect(() => console.log(item), [item]);
...
return (
<div>
{item.map(({ id, name }) => (
<p key={id}>{name}</p>
))}
</div>
);
You need to return the value from inside .map method like:
return (<p>{name}</p>)
You need to return the JSX elements in your map.
const Comp= () => {
const [item, setItem] = React.useState([]);
React.useEffect(() => {
fetch('https://api.punkapi.com/v2/beers')
.then((res) => res.json())
.then((data) => {
setItem(data);
})
.catch((err) => {
console.log(err);
});
}, []);
return (
<div>
{item.map((beer) => {
const { name, tagline } = beer;
return <p>{name}</p>;
console.log(name);
})}
</div>
);
};

How to assign data from a subcollection to its collection and render the new object? Using react, firebase and useeffect-hook

I got stuck with the following and haven't found any answer after a lot of research.
What I want to do: simply getting users inluding their images from a firestore-DB with react and the useeffect-hook and displaying them.
The DB-structure looks as follows:
https://i.stack.imgur.com/sDcrv.png
So the pictures are a subcollection of the users-collection.
After getting the users from the users-collection, I'm doing a second request for adding the users images to this specific user using Object.assign. After every forEach-run over the users-collection I'm setting the users-array with setUsers((oldUsers) => [...oldUsers, currentUser]);. Logging the users-array shows uses INCLUDING their images.
The problem: When trying to render the images, they are always undefined.
Workaround: Pressing a button that calls a function for re-setting the users:
const reRenderUsers = () => {
if (userDataLoaded === false) {
setUserDataLoaded(true);
}
const copy = [...users];
setUsers(copy);
};
^ This solves the problem and all images where shown.
Question: Is there any possibility showing the images instantly without the need of "re-rendering" the users? Am I using the useEffect-hook wrong for example? I'm thankful for any advice. Many thanks in advance!
Here the full code:
const [users, setUsers] = useState([]);
const [userDataLoaded, setUserDataLoaded] = useState(false);
useEffect(() => {
const unsubscribe = database.collection("users").onSnapshot((snapshot) => {
snapshot.forEach((doc) => {
const currentUser = {
id: doc.id,
...doc.data(),
};
database
.collection("users")
.doc(currentUser.id)
.collection("pictures")
.get()
.then((response) => {
const fetchedPictures = [];
response.forEach((document) => {
const fetchedPicture = {
id: document.id,
...document.data(),
};
fetchedPictures.push(fetchedPicture);
});
currentUser.pictures = [];
Object.assign(currentUser.pictures, fetchedPictures);
})
.catch((error) => {
console.log(error);
});
setUsers((oldUsers) => [...oldUsers, currentUser]);
});
});
return () => {
unsubscribe();
};
}, []);
const reRenderUsers = () => {
if (userDataLoaded === false) {
setUserDataLoaded(true);
}
const copy = [...users];
setUsers(copy);
};
return (
<div>
{!userDataLoaded ? (
<button onClick={reRenderUsers}> load users </button>
) : null}
{users.map((user, index) => (
<div key={user.id}>
{user.pictures && <img src={user.pictures[0].imageUrl}></img>}
</div>
))}
</div>
);
}
export default User;
This is because you are calling setUser before the firebase response completes the callback chain. You need to update the state right after the loop inside the success callback completed. I have updated useEffect to update it right after the callback
useEffect(() => {
const unsubscribe = database.collection("users").onSnapshot((snapshot) => {
snapshot.forEach((doc) => {
const currentUser = {
id: doc.id,
...doc.data(),
};
database
.collection("users")
.doc(currentUser.id)
.collection("pictures")
.get()
.then((response) => {
const fetchedPictures = [];
response.forEach((document) => {
const fetchedPicture = {
id: document.id,
...document.data(),
};
fetchedPictures.push(fetchedPicture);
});
currentUser.pictures = fetchedPictures;
setUsers((oldUsers) => [...oldUsers, currentUser]);
})
.catch((error) => {
console.log(error);
});
//dont need this here
//setUsers((oldUsers) => [...oldUsers, currentUser]);
});
});
return () => {
unsubscribe();
};
}, []);
Good Luck

What is the problem in React hook when retrieve data from API

I have the following function component
import React ,{useState,useEffect} from "react";
export default function Statistics() {
const [data, setData] = useState([]);
useEffect(() => {
fetch("https://hn.algolia.com/api/v1/search")
.then(response => response.json())
.then(data => setData(data));
}, []);
return (
data.map(el =>
<div>{el.title}</div>
)
);
}
I get the error:
Uncaught TypeError: data.map is not a function
what is the problem in the previous code ?
Just checked the API endpoint what you are working with. It seems you need the hits property because that's an array. In your current solution data is an object which does not have .map() to call. If you modify from data to data.hits then you can iterate through the array with .map().
Try the following:
useEffect(() => {
fetch("https://hn.algolia.com/api/v1/search")
.then(response => response.json())
.then(data => setData(data.hits));
}, []);
I hope this helps!

Categories

Resources