For some reason when I am updating my state from the useEffect hook it is not persisting. I am expecting that my list of users will keep growing, but instead I am only seeing one user. Why is users not growing?
import { useState, useEffect } from "react";
interface IUser {
name: {
first: string;
last: string;
};
picture: {
thumbnail: string;
};
}
function App() {
const [users, setUsers] = useState<IUser[]>([]);
const getUserName = (user: IUser): string => {
if (!user) return "";
return `${user.name.first} ${user.name.last}`;
};
const getThumbnail = (user: IUser): string => {
if (!user) return "";
return user.picture.thumbnail;
};
useEffect(() => {
setInterval(() => {
fetch("https://randomuser.me/api")
.then((res) => res.json())
.then((json) => {
let newusers = [...users, json.results[0]];
setUsers(newusers);
})
.catch((err) => {
console.log(err);
});
}, 3000);
}, []);
return (
<div className="App">
{users?.map((user, idx) => (
<div key={idx}>
<h2>{getUserName(user)}</h2>
<img src={getThumbnail(user)} />
</div>
))}
</div>
);
}
export default App;
Here is a codesandbox demonstrating the issue https://codesandbox.io/s/react-typescript-forked-5h3553?file=/src/App.tsx:0-1054
CodePudding user response:
The problem comes from the use of setInterval
. When it is defined, it creates a closure including the current value of the variable users
which of course starts as []
. So every time the interval is invoked, it is using the value it had at the time of instantiation.
Luckily, calls to setState pass a parameter of the previous state, which you can use to quickly fix your problem:
.then((json) => {
setUsers((prev) => [...prev, json.results[0]]);
})
CodePudding user response:
You need to use the method returned from useState hook to update the array.
Here is the solution:
let newuser = json.results[0];
setUsers(prev => [...prev, newuser]);
Instead of:
let newusers = [...users, json.results[0]];
setUsers(newusers);
CodePudding user response:
Just update setUsers
directly like this:
setUsers(prevState => […prevState, json.results[0]]);