I am trying to learn hooks and the useState
method has made me confused. I am assigning an initial value to a state in the form of an array. The set method in useState
is not working for me, both with and without the spread operator.
I have made an API on another PC that I am calling and fetching the data which I want to set into the state.
Here is my code:
<div id="root"></div>
<script type="text/babel" defer>
// import React, { useState, useEffect } from "react";
// import ReactDOM from "react-dom";
const { useState, useEffect } = React; // web-browser variant
const StateSelector = () => {
const initialValue = [
{
category: "",
photo: "",
description: "",
id: 0,
name: "",
rating: 0
}
];
const [movies, setMovies] = useState(initialValue);
useEffect(() => {
(async function() {
try {
// const response = await fetch("http://192.168.1.164:5000/movies/display");
// const json = await response.json();
// const result = json.data.result;
const result = [
{
category: "cat1",
description: "desc1",
id: "1546514491119",
name: "randomname2",
photo: null,
rating: "3"
},
{
category: "cat2",
description: "desc1",
id: "1546837819818",
name: "randomname1",
rating: "5"
}
];
console.log("result =", result);
setMovies(result);
console.log("movies =", movies);
} catch (e) {
console.error(e);
}
})();
}, []);
return <p>hello</p>;
};
const rootElement = document.getElementById("root");
ReactDOM.render(<StateSelector />, rootElement);
</script>
<script src="https://unpkg.com/@babel/standalone@7/babel.min.js"></script>
<script src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
Neither setMovies(result)
nor setMovies(...result)
works.
I expect the result variable to be pushed into the movies array.
Best Answer
Much like setState in Class components created by extending
React.Component
orReact.PureComponent
, the state update using the updater provided byuseState
hook is also asynchronous, and will not be reflected immediately.Also, the main issue here is not just the asynchronous nature but the fact that state values are used by functions based on their current closures, and state updates will reflect in the next re-render by which the existing closures are not affected, but new ones are created. Now in the current state, the values within hooks are obtained by existing closures, and when a re-render happens, the closures are updated based on whether the function is recreated again or not.
Even if you add a
setTimeout
the function, though the timeout will run after some time by which the re-render would have happened, thesetTimeout
will still use the value from its previous closure and not the updated one.If you want to perform an action on state update, you need to use the useEffect hook, much like using
componentDidUpdate
in class components since the setter returned by useState doesn't have a callback patternAs far as the syntax to update state is concerned,
setMovies(result)
will replace the previousmovies
value in the state with those available from the async request.However, if you want to merge the response with the previously existing values, you must use the callback syntax of state updation along with the correct use of spread syntax like