#[solved] fetcher.load() causes all useLoaderData() to change internally, triggering a rerender

1 messages · Page 1 of 1 (latest)

slim dew
#

Hi everyone,
I'm having a performance issue with a simple combination of a fetcher.load() and useLoaderData(). I tried boiling it down to this simple example:

export const loader = () => {
  return { foo: "bar" };
};

export default function Index() {
  const data = useLoaderData();

  return (
    <>
      <ComponentWithFetcher />
      <VeryComplexComponent />
    </>
  );
}

const VeryComplexComponent = () => {
  return <p>I'm a very complex component!</p>;
};

const ComponentWithFetcher = () => {
  const fetcher = useFetcher();

  return (
    <fetcher.Form method="get" action="dummy">
      <button type="submit">click me</button>
    </fetcher.Form>
  );
};

The dummy "endpoint" simply looks like this:

export const loader = () => {
  return { message: "Hello World" };
};

The issue is, that on submitting the Form in the ComponentWithFetcher the useLoaderData of the Index component changes. The return value of useLoaderData doesn't change, it's just being reexecuted due to some internal change of context. This triggers a rerender of the whole Index component and thus of the VeryComplexComponent. The behavior is the same when using fetcher.load() instead of a Form.

In my "real world" code I have lots of fetchers deep down the react tree and a loader on the very top which is used to set a Context. This is highly inefficient, as all of the fetchers are causing a rerender of the whole page. Is there a way to avoid this behavior?
I hope I provided clear enough instructions on how to reproduce the issue, otherwise just let me know. Thanks!

pseudo glacier
slim dew
#

Hi Gus,
unfortunately shouldRevalidate is not the solution. There is no revalidation happening after a GET request from a fetcher. I've tried it myself to make sure this is not the issue but the loader is not running again (just the hook).

obsidian magnet
#

Fetchers are globally available via context for pending/optimistic UI reasons (i.e., useFetchers) so the top-down re-render is currently unavoidable. I would look into memoizing the expensive components: https://react.dev/reference/react/memo

slim dew
#

I really hoped this wouldn't be the answer 😄 but thanks, that helps a lot!