Troubles with using hook useState() - reactjs

Trying to use fetched data into hook useState(fetchedData)
const [car, setCar] = useState({images: []});
useEffect( () => {
fetchOneCar(id)
.then(data => setCar(data))
.finally(() => setLoading(false))
},[id]);
const [images,setImages] = useState(car.images)
console.log(images) // -> [] unpredictably empty
console.log(car.images) // -> [{},{},{}] (fetched data)
How to properly set data into useState() in my case ?

ok look first car is {images:[]}
then images is []
and then car turns into whatever data you fetched in use effect
just because you declare useState after use effect doesn't mean it will run after useEffect.
First all the useStates run and then the effects. that's the law.
so there is no unexpected result.
To fix this in yur use effect do this:
useEffect( () => {
fetchOneCar(id)
.then(data => {
setCar(data);
setImages(data)
})
.finally(() => setLoading(false))
},[id]);

According to your code, I expect that you want to fill the images with the result from data. If it is, then you have to put the setImages(data.images) inside the resolved promise, after the setCar(data).
It should be like this one
const [car, setCar] = useState({images: []});
const [images,setImages] = useState();
useEffect( () => {
fetchOneCar(id)
.then(data => {
setCar(data);
setImages(data.images);
})
.finally(() => setLoading(false))
},[id]);
I put the useState() for images at the top for better reading.

Related

Multiple API Calls with Promise.all

I'm trying to get different data from different APIs. I don't have any problem when getting one and updating state. But I can't figure out how can I update two different state with Promise.all()
How can I make this code work.
const [stats, setStats] = useState(null);
const [info, setInfo] = useState(null);
React.useEffect(()=>{
Promise.all([
fetch('https://api.opensea.io/api/v1/collection/nickelodeon-rugrats-heyarnold-eth/stats'),
fetch('https://api.opensea.io/api/v1/asset_contract/0x223E16c52436CAb2cA9FE37087C79986a288FFFA')])
.then(res =>Promise.all(res.map(r=> r.json())))
.then((stats) => {
setStats(stats);
})
.then((info) => {
setInfo(info);
})
.then(data => console.log(data)).catch(error => console.log(error));
},[])
The Promise.alls are resolving to an array of resolve values, so the later .thens should use that array - the two URLs being fetched doesn't result in two separate .thens, it only results in a single Promise that you need to extract two properties out of.
You can also make the code simpler and DRYer by starting with an array of the two URLs and mapping over them.
React.useEffect(() => {
const urls = ['https://api.opensea.io/api/v1/collection/nickelodeon-rugrats-heyarnold-eth/stats', 'https://api.opensea.io/api/v1/asset_contract/0x223E16c52436CAb2cA9FE37087C79986a288FFFA'];
Promise.all(urls.map(url => fetch(url).then(r => r.json())))
.then(([stats, info]) => {
setStats(stats);
setInfo(info);
})
.catch(error => console.log(error));
}, []);

How to fill users variable with data from GET request in React

I am making a GET call request to an API in order to fetch some data in my React component and save it to my users variable, but the data does not get saved, I get undefined when I console.log it. How can I set the response data from my GET call and save it in the users variable? Here is my attempt:
const [users, setPosts] = React.useState([]);
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/users')
.then((response) => response.json())
.then((data) => console.log(data));
setPosts(users.data);
}, []);
In the code you have posted, you aren't saving the data to users. Try this:
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/users')
.then((response) => response.json())
.then(setPosts);
}, []);
This is how it should be do it:
const [users, setPosts] = React.useState([]);
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/users')
.then((response) => response.json())
.then((data) => setPosts(data););
}, []);
That way you set your users as data
Also as a hook rule you should define your state as follow const [users, setUsers] = useState()

how to use a hook and avoid a loop

I want the fetchTasks() function to be called when I start the component, I know that componentDidMount() is used with classes, but in this way useEffect is used, when I use it, I enter a loop, although it does not send me warnings or errors it sends constant requests to API.
const [tasks, setTasks] = useState([]);
const fetchTasks = (e) => {
fetch('/api/tasks')
.then(res => res.json())
.then(data => {
setTasks(data)
console.log(tasks);
})
.catch(err => console.error(err))
}
useEffect(() => {
fetchTasks();
})
I only want to get the API data once when rendering the component.
Would it be correct to take advantage of this loop to use it as a socket with the API?
Thank you very much, I haven't been in React long.
Use square brackets in useEffect like
useEffect(() => {
fetchTasks();
},[])
If you want to call this again on change of anystate then call like
useEffectt(() => {
fetchTasks();
},[state_variable_name])
To only fetch your data onces inside useEffect you have to provide an empty dependancy array.
useEffect(() => {
fetchTasks();
}, [])

How to update state array fetched from API in React Hooks?

I'm fetching data from Studio Ghibli API and I am able to successfully fetch it, set the state array of objects and render it in my presentational component. However, I'm trying to create a function which will add new property "keyword" to every object in my state array. The problem is that when i try to copy the state array to manipulate it in my createKeywords function, the returned copy is empty and I'm unable to manipulate it after it being set.
This is the relevant code:
const baseUrl = 'https://ghibliapi.herokuapp.com/'
const [hasError, setErrors] = useState(false)
const [movies, setMovies] = useState([])
useEffect(() => {
fetch(baseUrl + 'films')
.then((res) => res.json())
.then((res) => {
console.log(res);
setMovies(res)
createKeywords()
})
.catch(err => setErrors(true));
}, [])
const createKeywords = () => {
const moviesWithKeywords = [...movies]
moviesWithKeywords.forEach(function(movie){
movie.keyword = 'castle'
});
setMovies(moviesWithKeywords)
}
If i don't call the createKeywords function everything works fine but obviously copying and setting new movies state creates problem. I tried adding [movies] instead of empty array in useEffect and that works but then useEffect runs indefinitely. Thank you in advance, React isn't my strong suit!
The solution seems might not be very obvious. There are cases where setMovies (in general setting the state) is an async operation, which means that even if you setMovies the movies variable is not being updated quite fast and therefore you are already executing the createKeawords function. This means that within the keywords function the movies variable didn't have the chance to update fast enough. I would recommend to pass the res as a parameter in the createKeywords and use this variable to copy the array to the moviesWithKeywords.
Have a look here under the section State Updates May Be Asynchronous
So do something like that:
const baseUrl = 'https://ghibliapi.herokuapp.com/'
const [hasError, setErrors] = useState(false)
const [movies, setMovies] = useState([])
useEffect(() => {
fetch(baseUrl + 'films')
.then((res) => res.json())
.then((res) => {
console.log(res);
setMovies(res)
createKeywords(res)
})
.catch(err => setErrors(true));
}, [])
const createKeywords = (movies) => {
const moviesWithKeywords = [...movies]
moviesWithKeywords.forEach(function(movie){
movie.keyword = 'castle'
});
setMovies(moviesWithKeywords)
}

React state hook doesn't properly handle async data

I'm trying to set a component's state through an effect hook that handles the backend API. Since this is just a mock, I'd like to use the vanilla react methods and not something like redux-saga.
The problem is that while the fetching part works, the useState hook doesn't update the state.
const [odds, setOdds] = useState({})
useEffect(() => {
(async () => {
fetchMock.once('odds', mocks.odds)
let data = await fetch('odds').then(response => response.json())
setOdds(data)
console.log(odds, data) // {}, {...actual data}
})()
}, [])
I've tried to pipe the whole process on top of the fetch like
fetch('odds')
.then(res => res.json())
.then(data => setOdds(data))
.then(() => console.log(odds)) // is still {}
But it doesn't make a single difference.
What am I doing wrong?
Basically if you call setOdds, the value of odds does not change immediately. It is still the last reference available at decleration of the hook.
If you want to access the new value of odds after updating it, you would have to either use the source of the updated value (data) if you want to access the value in the same useEffect hook or create another useEffect hook that triggers only when odds has changed:
useEffect(() => {
console.log(odds);
// Do much more
}, [odds]) // <- Tells the hook to run when the variable `odds` has changed.
If you want to see that state has changed in here, you can use
const [odds, setOdds] = useState({})
useEffect(() => {
(async () => {
fetchMock.once('odds', mocks.odds)
let data = await fetch('odds').then(response => response.json())
setOdds(prevData => {
console.log(prevData, data) // {}, {...actual data}
return data
})
})()
}, [])

Resources