Reputation: 1159
I'm trying to update a state inside a useEffect
, I'm getting:
React Hook
useEffect
has a missing dependency: 'user'. Either include it or remove the dependency array. You can also do a functional update 'setUser(u => ...)' if you only need 'user' in the 'setUser' call react-hooks/exhaustive-deps
If I put user state as a dependency then I get an infinite loop.
const [user, setUser] = useState({
displayName: '',
hasPassword: false,
...
});
let { userId } = useParams();
useEffect(() => {
const checkPassword = async (userId) => {
try {
const res = await UserService.checkPassword(userId);
if (res.status === 200) {
setUser({...user, hasPassword: res.data.hasPassword });
}
} catch (err) {
setUser({...user, hasPassword: false });
}
};
checkPassword(userId);
}, [userId, user]);
If I remove user from dependency array than everything works fine, but I'm trying to get rid of that warning. I just need to execute this function only once.
Upvotes: 0
Views: 176
Reputation: 2558
Since you have circular dependancy i.e.
user
object changesuser
object againNote that, reference of your user
object changes on every setUser
call, that's why there's infinite loop.
I wouldn't recommend having user
in your dependancy array as you could update user
details if userId
changes.
If still you can't remove
user
dependancy, you could useJSON.stringify(user)
as dependancy instead as the effect would run only when stringified representation of youruser
object changes.
useEffect(() => {
// your effect code
}, [userId, JSON.stringify(user)]);
Note: Use stringify()
only if you don't have values with undefined/NaN etc as the keys would be lost after conversion.
Upvotes: -1
Reputation: 15176
I guess you can use the previous state of the user
instead of the state user
as:
setUser(prevState => ({
...prevState,
hasPassword: res.data.hasPassword
}));
Thus you don't need user
in the dependency array.
Final code would be with the suggestion:
useEffect(() => {
const checkPassword = async (userId) => {
try {
const res = await UserService.checkPassword(userId);
if (res.status === 200) {
setUser(prevState => ({
...prevState,
hasPassword: res.data.hasPassword
}));
}
} catch (err) {
setUser(prevState => ({
...prevState,
hasPassword: false
}));
}
};
checkPassword(userId);
}, [userId]);
Upvotes: 1
Reputation: 20007
You can use the setState callback syntax:
setUser(user => ({ ...user, hasPassword: whatever }))
Upvotes: 0
Reputation: 976
The method returned on the second position from useState
also accepts a callback, so your code could be rewritten as this:
const [user, setUser] = useState({
displayName: '',
hasPassword: false,
...
});
let { userId } = useParams();
useEffect(() => {
const checkPassword = async (userId) => {
try {
const res = await UserService.checkPassword(userId);
if (res.status === 200) {
setUser(user=>({...user, hasPassword: res.data.hasPassword }));
}
} catch (err) {
setUser(user=>({...user, hasPassword: false }));
}
};
checkPassword(userId);
}, [userId]);
Upvotes: 1