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

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)
}

Related

Using a POST request's response values in React Native

everyone. I'm a new developer and this is my first post here, so please bear with me.
What I'm trying to do is access the values that I received from a POST request that I made.
Below you can see what I'm trying to do. I set up a state where I'll store my data and then there's a function called fetchData that does a POST request and receives an object with the values, right?
Now I that I've received my object, I want to store its values in some of my variables.
Since the values have been stored in the 'data' state, I thought I would access its values by doing something like 'userName = data.userName', or something like 'age = data.userAge'. Obviously that didn't work out because my IDE does not know those values yet lol. So how do you think I should access them?
const [data, setData] = useState([{}]);
useEffect(() => {
fetchData({
ids: [0],
})
.then((response: any) => {
setData(response);
})
.catch((error: any) => {
console.log('error', error);
});
}, []);
dont place the function directly in useEffect, do something like this instead
const [data, setData] = useState([])
const getData = async() => {
try{
const response = await fetchData({ids: [0]});
setData(response);
}catch(error){
console.log(error);
}
}
useEffect(() => {
getData();
},[])

useEffect is causing infinite loop when use state as dependency

Here simply I am fetching data from mysql DB and storing it in state and in order to fetch this data:
const [orders, setOrders] = useState([]);
To fetch data I am using different functions and finally I am calling those functions using useEffect simple enough and so for everything is working perfectly but the problem comes whenever I use the state as dependency where I am storing data beacause if I dont do that then I have to manually refresh the page for latest changes and I have tried every given solution on stackoverflow but any of the solution didnt work so someone can please help me how can I use this state as dependencey without causing infinite loop:
const [orders, setOrders] = useState([]);
const loadData = async () => {
const response = await fetch("http://localhost/k-shop/load.php");
const result = await response.json();
setOrders(result);
};
const loadTotal = async () => {
const response = await fetch("http://localhost/k-shop/amount.php");
const result = await response.json();
setTotal(result);
};
useEffect(() => {
loadData();
loadTotal();
}, [orders]);
console.log(orders);
If you move the state into the useEffect dependency, you can then check if it is empty, and only set it when that check passes.
It will set the state once to populate and not pass the check again.
const [orders, setOrders] = useState([]);
const loadData = async () => {
const response = await fetch("http://localhost/k-shop/load.php");
const result = await response.json();
setOrders(result);
};
const loadTotal = async () => {
const response = await fetch("http://localhost/k-shop/amount.php");
const result = await response.json();
setTotal(result);
};
useEffect(() => {
if(orders.length === 0) {
loadData();
}
// you can do the same with checking loadTotal() state
}, [orders]);
console.log(orders);
Avoid ,non-primitive data types in dependencyArray ,
useEffect(() => {
loadTotal();
loadData();
}, [total, orders.length]);
every times you "setOrders" means you change the state,every times you change the state,means the "useEffect" will do again.that cause infinite loops.why not try useEffect(() => {loadData()}, [])?

Troubles with using hook useState()

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.

useEffect not triggering but the template is being rendered somehow

I am getting too many re-renders while using react-hooks.
I am trying to fetch data from api by using a parameter in URL.
Here's the code:
export default function Details() {
const { title } = useParams();
const [loading, setLoading] = useState(true);
const [details, setDetails] = useState([]);
const [error, setError] = useState("");
function getDetails(keyword) {
if (keyword) {
setLoading(true);
fetch(
`API`
)
.then((res) => {
let result = res.data.results;
result = result.filter(function (result) {
return (result.title = keyword);
});
setDetails(result[0]);
setLoading(false);
console.log(details);
})
.catch((err) => {
setError(["Unable to fetch data"]);
setLoading(false);
});
}
}
getDetails(title)
return(
// template
)
now I think this is happening at the line where I call getDetails.
then I tried using useEffect to load the data only once after it is mounted,
useEffect(() => {
getDetails(title);
}, []);
It still is unable to fetch the data, as the getDetails function is never called.
How can I resolve this?
Edit:
Fixed one silly error.
Here's the codesandbox link:
Codesandbox
There are multiple issues with this, first you need to specify what you want to be notified about when the useEffect gets called again. You could do this by adding the variables you want within the array
useEffect(() => {
getDetails(title);
}, [
// Add what you want here
]);
The second issue you have is that you declared the detalis variable twice. Once using the set state here: const [details, setDetails] = useState([]);
The second time here:
const details = getDetails(title)
the code here has two obvious error beside the functionality problems you mentioned:
1 - you cannot declare two variables with same name using let or const; it will throw a SyntaxError
const [details, setDetails] = useState([]);
...
const details = getDetails(title)
2- getDetails function is written with a asynchronous mindset, and it will return nothing,
so details in const details = getDetails(title) will be set to undefined
Looks like your getDetails function has title param so I would add title and getDetails both in the dependency list of useEffects
Or place getDetails inside the useEffect
Here is your working code. You had multiple problems where res.data was undefined so you need to get res.results directly based on your response object
useEffect(() => {
function getDetails(keyword) {
if (keyword) {
setLoading(true);
fetch(
`https://api.jikan.moe/v3/search/anime?q=${keyword}&page=1&genre_exclude=0`
)
.then((res) => res.json())
.then((res) => {
console.log(res.results);
let result = res.results;
console.log(result);
result = result.filter(function (result) {
return (result.title = keyword);
});
setDetails(result[0]);
setLoading(false);
console.log(3);
})
.catch((err) => {
console.log(err);
setError(["Unable to fetch data"]);
setLoading(false);
});
}
}
console.log('calling getDetails')
getDetails(title);
}, [title]);
Note: tested in the code sandbox link provided in the question. Its working code.

How to stop useEffect from inifite loop when first setting state and then using state

There are plenty of questions about this topic already but none I could see that both set the state then use that same state.
I am setting state on the first run and then passing the state back in via the array. In theory, this should run only 2 times, once when data1 and data2 are blank and then again when they change to updated.
Passing in the state this way makes it run forever currently. How do I stop this? The data is not updating so why is it running over and over? Or do this another way?
I don't want combineData running each time a change is made as it is not necessary. I want this to take place on load only (like I am trying to do here).
const [data1, setData1] = useState('')
const [data2, setData2] = useState('')
const [data, setData] = useState('')
useEffect(() => {
///SET STATE
data1
.getData1(getData1Func)
.then((res) => setData1(res))
data2
.getData2(getData2Func)
.then((res) => setData2(res))
///USE STATE
combineData(data1, data2)
}, [data1, data2]) // pass in an array as a second argument
const combineData = (...args) => {
let data = [...args].flat()
setData(data)
}
You are updating the state, yet you are using those state as dependency, so it becomes a infinite loop.
If you only want to use it once then remove the dependencies.
useEffect(() => {
// your codes.
},[])
If not, then there's not necessary to use state, instead, use local variables.
useEffect(() => {
///SET STATE
const initiate = async () => {
const local1 = await getData1()
const local2 = await getData2()
combineData(local1, local2)
}
initiate();
}, []) // pass in an array as a second argument
const combineData = (...args) => {
let data = [...args].flat()
setData(data)
}

Resources