question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Calling setState after on useQuery data triggers infinite loop

See original GitHub issue

I’ve been using apollo-hooks in my app and so far I love it except I can’t get pass this issue.

I’m using useQuery to return an array of users to display in a list. However I want to store those users in local state using useState so that when I click a delete button next to the user it deletes them from the list. However whenever I call useState on data from apollo-hooks` I get this error.

Invariant Violation: Too many re-renders. React limits the number of renders to prevent an infinite loop.

I’m not sure what I’m doing wrong, any insight would be helpful. Here’s some of my code.

const [user, setUser] = useState('');
  const [list, setList] = useState([]);

  useEffect(() => {
    if (storage.hasUser()) {
      const user = storage.getUser();
      setUser(user);
    } else {
      history.push('/login');
    }
  }, []);

  const { accessToken, organizationId } = user;

  const { data, error, loading } = useQuery(GROUPS_PAGE_QUERY, {
    variables: {
      accessToken: accessToken,
      organizationId: organizationId
    }
  });

  if (data) {
    setList(data); // this seems to be where the error is happening. 
  }

return (
    <div>
      <GroupList groups={data.userGroups} deleteGroup={deleteGroup} />
    </div>
  );

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:11
  • Comments:12

github_iconTop GitHub Comments

25reactions
ThomasK33commented, Apr 21, 2019

This is a React Hooks misunderstanding and not an issue with this library.

Each set[...] call (more detailed the second element in the array) from a useState causes a re-render of the component containing the hook.

Let’s apply this to your code:

const [user, setUser] = useState('');
const [list, setList] = useState([]);

  useEffect(() => {
    if (storage.hasUser()) {
      const user = storage.getUser();
      setUser(user);
    } else {
      history.push('/login');
    }
  }, []);

This effect call is going to be executed once. If the storage has a user it will set the user and cause the component to re-render with the set user. In this new re-render, this effect wont be called again as the deps array did not change any values.

  const { accessToken, organizationId } = user;

  const { data, error, loading } = useQuery(GROUPS_PAGE_QUERY, {
    variables: {
      accessToken: accessToken,
      organizationId: organizationId
    }
  });

A query is executed with the useQuery function. useQuery is a hook and will return an object containing a response.

Assuming the data is not going to change on the server and / or the local cache, this response will always result the same data. (considering same query && params)

  if (data) {
    setList(data); // this seems to be where the error is happening. 
  }

 [...]

As you correctly pointed out, an infinite re-render loop starts in this line.

If one finished loading (loading == false) and there have been no errors (error == null), one’s data might not be null and might be set.

Assuming data != null, this condition will be true on every subsequent re-render as the server / cached response will not change.

Hence one ends up with an infinite loop, as setList calls will trigger a re-render that check for data != null and trigger a setList calls that re-renders (and so on…).


To fix this issue, either directly use the data variable from the useQuery hook or consider using a React Ref.

Also if you do not require the data from the useQuery in this component, don’t query it. Query it with the component that is going to use it.


On a side note: the workaround presented above is more of a hack than dealing with the elephant in the room. So while it’s true, that by using memoization one can manage to get out of the re-render cycle, it is a “fix” to a misconception in the use of hooks.

17reactions
JaosnHsiehcommented, Apr 18, 2019

I encountered the same problem, this is my workaround. use useMemo.

In your example:

import React, { useState, useMemo, useEffect } from 'react';

...

   const { data, error, loading } = useQuery(GROUPS_PAGE_QUERY, {
    variables: {
      accessToken: accessToken,
      organizationId: organizationId
    }
  });

  useMemo(()=>{
    setList(data.YOUR_DATA_NAME); 
  },[data])

...

Read more comments on GitHub >

github_iconTop Results From Across the Web

Prevent infinite renders when updating state variable inside ...
I am using useEffect hook to control how many times should "useQuery" be called. What I want to do is whenever I receive...
Read more >
How do you handle useQuery with useEffect? - Help
I need to edit a form with data coming from useQuery. My issue is that react imposes an order to execute hooks, and...
Read more >
5 useEffect Infinite Loop Patterns | by Naveen DA
In this code, we set the state value using setData on-network call success, it will trigger the component re-render. Since the useEffect ...
Read more >
React setState usage and gotchas - ITNEXT
It is invoked internally by React during the update phase (props or state change). Calling setState here would result in an infinite loop...
Read more >
too many re-renders. react limits the number - You.com
The reason for the infinite loop is because something (most likely setState ) in the event callback is triggering a re-render. This will...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found