React Hooks react-hooks/exhaustive-deps eslint rule seems overly sensitive

Viewed 131

I am having trouble completely fully understanding dependencies in the linter rule react-hooks/exhaustive-deps. It seems to me that in many situations you do not need a specific variable in the array because you do not want the hook to be called when that variable changes. However, this linting rule will tell you that it is missing from the dependencies if it is inside of the hook. I understand that I can disable the line, but I am wondering if there is something I am misunderstanding about the rule, and if there would be a better way to code this functionality using hooks. Here is an example of code that I feel this rule does not need to be applied.

I have the following two state variables:

const [taggedUsers, setTaggedUsers] = useState([])
const [users, setUsers] = useState([])

taggedUsers represent the users who are associated with an entity (of which only the emails strings of users are saved to), whereas users are user objects fetched from another api which have more information than just the emails of users.

I have a component with a hook that hydrates taggedUser objects when called so I can display their names on the page. When I load the page, the endpoint I hit that gives me the entity has a property taggedUsers which is only a list of emails associated with the entity. I have a transformer that takes those emails and maps them to a list of taggedUser objects that look like this:

{ email: "john.smith@gmail.com", name: null, id: "john.smith@gmail.com" }

After hydration, the object will look like this:

{ email: "john.smith@gmail.com", name: "John Smith", id: "john.smith@gmail.com" }

I have an endpoint that I can hit to get a list of users, and I use the data fetched from there to hydrate the names of the user objects. When my component first renders, I fetch the users, and set the state of my variable "users" to those users. The change in users then triggers the next hook:

useEffect(() => {
const hydrateTaggedUsers = () => {
  const hydratedTaggedUsers = []
  users.forEach((user) => {
    taggedUsers.forEach((taggedUser) => {
      if (user.email === taggedUser.email) hydratedTaggedUsers.push(user)
    })
  })
  setTaggedUsers(hydratedTaggedUsers)
}

if (users.length) hydrateTaggedUsers()
}, [users])

I only want this hook to be called once when "users" changes, which is only once per page load. According to the linter setting, taggedUsers is missing from the dependencies of the hook. However, I do not want this hook to be called upon the value of taggedUsers changing, and adding it as a dependency causes a stack overflow because this hook changes the value of taggedUsers.

Is there something I am missing about how hooks should be constructed? It feels to me that since I do not want this hook to be called upon the value of taggedUsers being updated, it should not be included in the dependency array. However, the linter still wants it to be in there. Is this a situation where disabling the line is just inevitable?

1 Answers

Try something..

Did you have the full code so I can check this deeper?

I suggest this, not sure that its work :)

useEffect(() => {
 if (users.length) {
   const hydratedTaggedUsers = [];
   users.forEach(user => {
     taggedUsers.forEach(taggedUser => {
       if (user.email === taggedUser.email) hydratedTaggedUsers.push(user);
     });
   });
   setTaggedUsers(hydratedTaggedUsers);
 }
}, [users]);
Related