vercel / next.js

The React Framework
https://nextjs.org
MIT License
124.67k stars 26.61k forks source link

`useEffect` not working in root `loading.tsx` on initial loading #41972

Open lukemorales opened 1 year ago

lukemorales commented 1 year ago

Verify canary release

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 21.6.0: Wed Aug 10 14:28:23 PDT 2022; root:xnu-8020.141.5~2/RELEASE_ARM64_T6000
Binaries:
  Node: 16.14.2
  npm: 7.24.2
  Yarn: 1.22.19
  pnpm: 7.13.3
Relevant packages:
  next: 13.0.1-canary.0
  eslint-config-next: 13.0.0
  react: 18.2.0
  react-dom: 18.2.0

What browser are you using? (if relevant)

Arc Browser

How are you deploying your application? (if relevant)

No response

Describe the Bug

loading.tsx does not throw any errors if made a Client Component with 'use client' declarative, however, useEffect does not run on Loading components.

Expected Behavior

useEffect is execute.

Link to reproduction

https://stackblitz.com/edit/vercel-next-js-duighx?file=app/loading.tsx

To Reproduce

Create a loading file in /app directory and use the following code:

"use client";

import { useState, useEffect } from "react";

export default function LoadingSpinner() {
  const [dots, setDots] = useState(".");

  useEffect(() => {
    console.log("running effect...");
    const interval = setInterval(() => {
      console.log("running...");
      setDots((prevDots) => prevDots + ".");
    }, 100);

    return () => {
      clearInterval(interval);
    };
  }, []);

  return <p>Loading{dots}</p>;
}
The-Code-Monkey commented 1 year ago

@lukemorales i have the same issue apart from my data isnt being fetched in my case. useEffect not getting called in a client side component.

lukemorales commented 1 year ago

Just changed the title because I was able to see state updates happening on nested routes, and the root loading also updating if my App entry point was a nested route then navigating back to the / path. But on first load, the root loading.tsx is not behaving like a client component

JasperAlexander commented 1 year ago

useEffect is also not working in a client component that has a server component as child, imported from a server component.

mgmolisani commented 4 months ago

Similarly, I was just playing around with loading.tsx as a client component rendering a <progress> with a progress variable intended to update in setInterval setup in an effect. It fails to run at all on the client. Additionally, even console.log in the function body does not run (i.e. the component is only run on the server). Toggling the suspense boundary in React dev tools triggers the correct behavior (i.e. it finally client renders).

mgmolisani commented 4 months ago

Might have found some related content.

https://github.com/vercel/next.js/issues/44249

After reading that post, it seems to suggest that there is not enough JS loaded to client render/hydrate the app and this is a result of the page still streaming in with that runtime. However, we can clearly see the layout.tsx files have no issue being run as a client component and wraps this component so this seems like an issue specifically with how loading.tsx is rendered.

So I guess the follow up is either can this behavior be avoided like layout.tsx or is there a need for a new or changed API to account for this RSC only first load.

If there is a way to avoid it, then let’s implement it so we can seamlessly use React features when the loader is marked “use client”. It seems reasonable to expect it to run SSR and rehydrate on any suspension.

If we can’t avoid it, maybe we need a new API to signal developers that there is at first a render that cannot fully take advantage of the client and then subsequent loads can leverage client only. This might be similar to “error.tsx” vs “global-error.tsx” to create a server only loader. Or simply prohibit client loaders (which seems aggressively opinionated). Both options seems bad from either an implementation side or a DX side.