Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infinite loop when using fetcher.load in latest Remix releases #6072

Closed
1 task done
BrycePearce opened this issue Apr 15, 2023 · 6 comments · Fixed by #6121
Closed
1 task done

Infinite loop when using fetcher.load in latest Remix releases #6072

BrycePearce opened this issue Apr 15, 2023 · 6 comments · Fixed by #6121
Labels
bug Something isn't working

Comments

@BrycePearce
Copy link

BrycePearce commented Apr 15, 2023

What version of Remix are you using?

1.14.0 (also on 1.15)

Are all your remix dependencies & dev-dependencies using the same version?

  • Yes

Steps to Reproduce

1.) Create a resource-route (or any loader) that returns anything. In this case I'm returning json({hello: 'world'})
2.) In any route, create a state variable to track to see if it has been invoked. For example

  let fetcher = useFetcher();
  const [shouldCallResourceRoute, setShouldCallResourceRoute] = useState(true);

  useEffect(() => {
    if (shouldCallResourceRoute) {
      setShouldCallResourceRoute(false); // this should cause this fetcher to never load again. But it infinite loops
      fetcher.load("/resource-routes/resource");
    }
  }, [fetcher, shouldCallResourceRoute]);

3.) Note the infinite loop / crash that happens
image

-Note-

I am upgrading my Remix from an earlier version where my existing code worked fine. This bug only appeared after upgrading and I was able to replicate in a code sandbox here:

https://codesandbox.io/p/sandbox/hopeful-night-z2lsum?file=%2Fapp%2Froutes%2Findex.tsx&selection=%5B%7B%22endColumn%22%3A1%2C%22endLineNumber%22%3A7%2C%22startColumn%22%3A1%2C%22startLineNumber%22%3A7%7D%5D

Expected Behavior

The fetcher should only load once

Actual Behavior

An infinite loop occurs, crashing the page.

@chuanhhoang
Copy link

I face this problem too. Any update on this?

@BrycePearce
Copy link
Author

BrycePearce commented Apr 16, 2023

I face this problem too. Any update on this?

I believe this is an unlisted bug/change that only affects the latest versions. However there is a work around provided by brocktho in the discord chat

  useEffect(() => {
    if (shouldFetch && fetcher.state === 'idle') {
      fetcher.load('/fetchRoute');
      setShouldFetch(false);
    }
  }, [fetcher, shouldFetch]);

https://stackblitz.com/edit/remix-run-remix-ahjkhw?file=app%2Froutes%2F_index.tsx,app%2Fentry.server.tsx

Here's a summary of why this works from Ryan Florence

If you’re losing input focus something in your tree is unmounting the input or its parent
Fetchers are stable between renders, but change when their state changes, so if it’s loading/submitting in a useEffect you have to guard it by check it fetcher.state and sometimes fetcher.data, but this won’t fix your in mounting problem, that’s not Remix causing that. 
If possible, make your life easier and load/submit in event handlers instead of effects.

However as an unlisted change that breaks previously working code, I still believe this is a bug

@pkasarda
Copy link

pkasarda commented Apr 18, 2023

probably dup of #6014 + docs are here https://remix.run/docs/en/main/hooks/use-fetcher#fetcherload

function SomeComponent() {
  const fetcher = useFetcher();

  useEffect(() => {
    if (fetcher.state === "idle" && fetcher.data == null) {
      fetcher.load("/some/route");
    }
  }, [fetcher]);

  fetcher.data; // the data from the loader
}

vaynevayne added a commit to vaynevayne/react-router that referenced this issue Apr 18, 2023
vaynevayne added a commit to vaynevayne/react-router that referenced this issue Apr 18, 2023
@kiliman
Copy link
Collaborator

kiliman commented Apr 18, 2023

Yes, I believe that you should always check fetcher.state and fetcher.data and not rely on a stable fetcher reference.

@brophdawg11
Copy link
Contributor

This turns out to be due to the usage of useSyncExternalStore inside of react router and explains why this was not an issue prior to 1.10.0. The tl;dr; is in remix-run/react-router#10377, but this should be fixed once that gets merged and released.

@brophdawg11 brophdawg11 added the awaiting release This issue has been fixed and will be released soon label Apr 27, 2023
@brophdawg11 brophdawg11 removed their assignment Apr 27, 2023
@brophdawg11
Copy link
Contributor

This is fixed in 1.16.0-pre.1. I was having trouble getting the codesandbox above to actually install the new version but I recreated it locally and upgrading to 1.16.0-pre.1 fixed the issue. We're hoping to get the 1.16.0 stable release out in the next few days.

@brophdawg11 brophdawg11 removed the awaiting release This issue has been fixed and will be released soon label Dec 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
5 participants