auth0 / auth0-react

Auth0 SDK for React Single Page Applications (SPA)
MIT License
887 stars 258 forks source link

Setting isLoading to false in SSR mode causes server/client state mismatch #182

Closed bennettrogers closed 3 years ago

bennettrogers commented 3 years ago

Describe the problem

In auth-state.jsx, isLoading is initialized as false when in SSR mode. This makes it difficult to avoid a state mismatch warning between server and client rendering.

This error can be seen in the nextjs example in this repo. The Nav component checks the isLoading state to decide what to render. On the client isLoading correctly returns true, so the loading indicator is rendered until the authentication status can be determined. However in SSR mode, isLoading returns false, so the component instead renders the unauthenticated state. This throws a server/client mismatch error in the dev console.

It seems like isLoading should be initialized to true in both client and SSR modes. This would cause the Loading indicator to be rendered during SSR or static rendering when using Next.js, which would match the initial rendered state in client mode.

What was the expected behavior?

The content rendered in SSR mode should match the initially rendered state in the client mode.

Reproduction

This error can be reproduced by cloning this repository and running the nextjs-app example in the examples folder.

adamjmcgrath commented 3 years ago

Hi @bennettrogers - thanks for raising this

It seems like isLoading should be initialized to true in both client and SSR modes.

We set the isLoading flag to false when statically rendering the page because this accurately reflects the state of the library during SSR. It will only be loading when the SDK is launched in a browser.

I'm not able to reproduce this error in the nextjs example pap in the examples folder, could you share the full error details (message/stack) and also any other steps needed to reproduce it

bennettrogers commented 3 years ago

Here's how to reproduce:

...

  "dependencies": {
-    "@auth0/auth0-react": "file:../..",
+    "@auth0/auth0-react": "^1.2.0",
    "next": "9.4.4",
-    "react": "file:../../node_modules/react",
-    "react-dom": "file:../../node_modules/react-dom"
+    "react": "^17.0.1",
+    "react-dom": "^17.0.1"
  }
}
react-dom.development.js:67 Warning: Expected server HTML to contain a matching <div> in <div>.
    at div
    at Loading
    at Nav (webpack-internal:///./components/Nav.js:23:85)
    at Auth0Provider (webpack-internal:///./node_modules/@auth0/auth0-react/dist/auth0-react.esm.js:238:25)
    at MyApp (webpack-internal:///./pages/_app.js:51:94)
    at ErrorBoundary (webpack-internal:///./node_modules/@next/react-dev-overlay/lib/internal/ErrorBoundary.js:23:47)
    at ReactDevOverlay (webpack-internal:///./node_modules/@next/react-dev-overlay/lib/internal/ReactDevOverlay.js:73:23)
    at Container (webpack-internal:///./node_modules/next/dist/client/index.js:136:5)
    at AppContainer (webpack-internal:///./node_modules/next/dist/client/index.js:605:24)
    at Root (webpack-internal:///./node_modules/next/dist/client/index.js:692:25)

The error is because on the server, Next sees that isLoading is false and so it renders the Nav, whereas on the client isLoading is true, which renders the loading component. The discrepancy causes React to show its hydration warning. The warning could be suppressed by specifying suppressHydrationWarning, but that doesn't seem like the right solution.

adamjmcgrath commented 3 years ago

Hi @bennettrogers - I see what you mean

Let me have a think about what the implications would be to defaulting isLoading to true - it's simple enough to do, but my concern is it might require a new major release. You will have to live with the warning and the suppressHydrationWarning option for the time being.

vinamelody commented 3 years ago

Hi, if isLoading is initially true, how would this code be updated? The loading should be hidden initially

if (isLoading) {
    return <div>Loading...</div>;
  }
adamjmcgrath commented 3 years ago

@vinamelody - isLoading is set to false after the SDK is intialised

Is there a specific issue you are seeing with this? If so can you raise an issue with steps to reproduce it?