Calling setState after on useQuery data triggers infinite loop
See original GitHub issueI’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:
- Created 4 years ago
- Reactions:11
- Comments:12
Top 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 >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
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 auseState
causes a re-render of the component containing the hook.Let’s apply this to your code:
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.
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)
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 fordata != null
and trigger asetList
calls that re-renders (and so on…).To fix this issue, either directly use the
data
variable from theuseQuery
hook or consider using a React Ref.Also if you do not require the
data
from theuseQuery
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.
I encountered the same problem, this is my workaround. use
useMemo
.In your example: