I'm trying to figure out why my useEffect hook keeps getting called multiple times, even when the dependency has the same value. I'm using the following code:
import React, { useEffect, useState } from 'react'
import PropTypes from 'prop-types'
import Cards from '../../../cards/Cards'
import UserCard from '../../../cards/users/Card'
import LoadingContainer from '../../../LoadingContainer'
import UsersResource from '../../../../api/resources/Users'
const Users = ({ users }) => (
<Cards>
{users.map((user) => (
<UserCard user={user} key={`user-${user.id}`} />
))}
</Cards>
)
const UsersPage = () => {
const [initialLoad, setInitialLoad] = useState(true)
const [loading, setLoading] = useState(true)
const [initialUsers, setInitialUsers] = useState([])
const [users, setUsers] = useState([])
const fetchUsers = async () => {
setLoading(true)
const response = await UsersResource.getIndex()
setInitialUsers(response.data)
}
useEffect(() => {
fetchUsers()
}, [])
useEffect(() => {
console.log('users changed:', users)
initialLoad ? setInitialLoad(false) : setLoading(false)
}, [users])
useEffect(() => {
setUsers(initialUsers)
}, [initialUsers])
return (
<LoadingContainer
loading={loading}
hasContent={!!users.length}
>
<Users users={users} />
</LoadingContainer>
)
}
Users.propTypes = {
users: PropTypes.arrayOf(PropTypes.shape).isRequired,
}
export default UsersPage
This is the effect that gets re-run when the value of the users dependency stays the same:
useEffect(() => {
console.log('users changed:', users)
initialLoad ? setInitialLoad(false) : setLoading(false)
}, [users])
Here's the output:
users changed: []
users changed: []
users changed: (10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
So users is obviously being recognized as changed twice, even though both times the effect is called, it returns the same value. This results in my loading state being set to false before the request finishes.
It only runs once if I change the initial state assignment of users from this...
const [users, setUsers] = useState([])
To this...
const [users, setUsers] = useState(initialUsers)
This tells me that the component must be rerendering simply because users is pointing to initialUsers in the second effect, instead of just a blank array (even though initialUsers returns a blank array as well). Can anyone explain why this happens this way? I can't seem to find any documentation describing this behavior (maybe I'm blind).
I would expect the value to be the only thing to influence an effect, but it seems like it might get triggered because the dependency is pointing to a new reference in memory. Am I off?