relayjs / relay-examples

A collection of sample Relay applications
MIT License
1.14k stars 429 forks source link

Infinite re-renders from useSerializablePreloadedQuery #288

Open Dig-Doug opened 1 year ago

Dig-Doug commented 1 year ago

I ran into an issue with NextJS v13 useSerializablePreloadedQuery example which caused react to throw an error due to too many renders.

I have a setup similar to MainViewClientComponent, but I have:

  const [state, setState] = useState(null);
  const environment = useRelayEnvironment();
  const queryRef = useSerializablePreloadedQuery(
      environment,
      props.preloadedQuery
  );
  const [updatedQueryRef, loadQuery] = useQueryLoader(MyQuery, queryRef);

  return <>
    <Child queryRef={updateQueryRef} />
    <OtherChild setState={setState} />
  </>

My intention is to load the query from the server initially and when the user clicks a button load a new one. The state is not used the query logic.

The error occurs when the setState() function is called. I think what's happening is when the setState triggers a re-render, the useQueryLoader inputs also change and cause a second re-render. The second re-render causes React to detect an infinite render loop, throwing an error.

In my project, I have fixed (maybe? - I'm not a React+Relay expert) this issue by wrapping the return value in the useMemo call like so:

  return useMemo(() => {
    writePreloadedQueryToCache(preloadQuery);

    return {
      environment,
      fetchKey: preloadQuery.params.id ?? preloadQuery.params.cacheID,
      fetchPolicy,
      isDisposed: false,
      name: preloadQuery.params.name,
      kind: "PreloadedQuery",
      variables: preloadQuery.variables,
      dispose: () => {
        return;
      },
    };
  }, [preloadQuery]);

Is that the correct approach?