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

Update to HotReload triggers ‘bad setState()’ warning when server/client disagree on the initial value of useSyncExternalStore() and application throws #64936

Open
steveluscher opened this issue Apr 23, 2024 · 0 comments
Labels
bug Issue was opened via the bug report template.

Comments

@steveluscher
Copy link
Contributor

steveluscher commented Apr 23, 2024

Link to the code that reproduces this issue

https://github.com/steveluscher/hot-reload-bug-repro

To Reproduce

  1. Create a hook useFoo that uses useSyncExternalStore() where the initial value of the server and the client rendering mismatch (ie. getSnapshot and getServerSnapshot disagree).
    import { useSyncExternalStore } from "react";
    
    function getSnapshot() {
      return 1;
    }
    
    function getServerSnapshot() {
      return 0;
    }
    
    function subscribe() {
      return () => {};
    }
    
    export default function useExternalStoreWhereTheServerAndTheClientDisagreeOnFirstRender() {
      return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
    }
  2. In a component ComponentThatThrows, call useFoo and throw when the return value is the value returned on the client (ie. the value returned by getSnapshot()).
    import { ReactNode } from "react";
    import useExternalStoreWhereTheServerAndTheClientDisagreeOnFirstRender from "./useExternalStoreWhereTheServerAndTheClientDisagreeOnFirstRender";
    
    export default function ComponentThatThrows(): ReactNode {
      const num = useExternalStoreWhereTheServerAndTheClientDisagreeOnFirstRender();
      if (num === 1) {
        throw new Error("1 is the loneliest number");
      }
      return <span>Nothing is everything</span>;
    }
  3. Wrap the whole thing in an ErrorBoundary and supply a fallback rendering.
    <ErrorBoundary fallback={<>Something bad happened</>}>
      <ComponentThatThrows />
    </ErrorBoundary>

Current vs. Expected behavior

I would expect the server rendering to appear, followed by the error boundary's fallback once the app has a chance to re-render on the client.

I do see that, but in addition to that the following warning appears on the console:

Warning: Cannot update a component (HotReload) while rendering a different component (ComponentThatThrows). To locate the bad setState() call inside ComponentThatThrows, follow the stack trace as described in https://reactjs.org/link/setstate-in-render

Provide environment information

Operating System:
  Platform: linux
  Arch: x64
  Version: Ubuntu 20.04.0 LTS Tue Apr 23 2024 11:17:57 GMT-0700 (Pacific Daylight Time)
Binaries:
  Node: 18.18.0
  npm: 10.2.3
  Yarn: 1.22.19
  pnpm: 8.15.3
Relevant Packages:
  next: 13.5.1
  eslint-config-next: 13.5.1
  react: 18.2.0
  react-dom: 18.2.0
  typescript: 5.2.2
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

Not sure

Which stage(s) are affected? (Select all that apply)

next dev (local)

Additional context

No response

@steveluscher steveluscher added the bug Issue was opened via the bug report template. label Apr 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Issue was opened via the bug report template.
Projects
None yet
Development

No branches or pull requests

1 participant