astoilkov / use-local-storage-state

React hook that persists data in localStorage
MIT License
1.17k stars 41 forks source link

Hydration error with ssr:true using NextJS #50

Closed nepomuc closed 2 years ago

nepomuc commented 2 years ago

Using next 12.1.6 and react 18.1.0 I get a hydration error when using use-local-storage-state, even though I'm using the ssr:true option.

I'm setting it up like that:

const [introductionFinished, setIntroductionFinished] = useLocalStorageState(
  "introductionFinished",
  {
    ssr: true,
    defaultValue: false,
  }
);

And I'm using it like that to show a div when introductionFinished is true:

{introductionFinished ? (
  <div className="row">
    <div className="col">
      <Link href="/introduction-part-1">
        <a>Show introduction again</a>
      </Link>
    </div>
  </div>
) : (
  ""
)}

Here's my error log:

next-dev.js?3515:25 Warning: Expected server HTML to contain a matching <div> in <main>.
    at div
    at main
    at div
    at Home (webpack-internal:///./pages/index.js:72:97)
    at MyApp (webpack-internal:///./pages/_app.js:46:27)
    at ErrorBoundary (webpack-internal:///./node_modules/next/dist/compiled/@next/react-dev-overlay/client.js:8:20746)
    at ReactDevOverlay (webpack-internal:///./node_modules/next/dist/compiled/@next/react-dev-overlay/client.js:8:23395)
    at Container (webpack-internal:///./node_modules/next/dist/client/index.js:323:9)
    at AppContainer (webpack-internal:///./node_modules/next/dist/client/index.js:825:26)
    at Root (webpack-internal:///./node_modules/next/dist/client/index.js:949:27)
window.console.error @ next-dev.js?3515:25
printWarning @ react-dom.development.js?ac89:86
error @ react-dom.development.js?ac89:60
warnForInsertedHydratedElement @ react-dom.development.js?ac89:10496
didNotFindHydratableInstance @ react-dom.development.js?ac89:11425
warnNonhydratedInstance @ react-dom.development.js?ac89:14266
tryToClaimNextHydratableInstance @ react-dom.development.js?ac89:14400
updateHostComponent$1 @ react-dom.development.js?ac89:20711
beginWork @ react-dom.development.js?ac89:22447
beginWork$1 @ react-dom.development.js?ac89:27381
performUnitOfWork @ react-dom.development.js?ac89:26513
workLoopSync @ react-dom.development.js?ac89:26422
renderRootSync @ react-dom.development.js?ac89:26390
performConcurrentWorkOnRoot @ react-dom.development.js?ac89:25694
workLoop @ scheduler.development.js?bcd2:266
flushWork @ scheduler.development.js?bcd2:239
performWorkUntilDeadline @ scheduler.development.js?bcd2:533
react-dom.development.js?ac89:14388 Uncaught Error: Hydration failed because the initial UI does not match what was rendered on the server.
    at throwOnHydrationMismatch (react-dom.development.js?ac89:14388:1)
    at tryToClaimNextHydratableInstance (react-dom.development.js?ac89:14401:1)
    at updateHostComponent$1 (react-dom.development.js?ac89:20711:1)
    at beginWork (react-dom.development.js?ac89:22447:1)
    at HTMLUnknownElement.callCallback (react-dom.development.js?ac89:4161:1)
    at Object.invokeGuardedCallbackDev (react-dom.development.js?ac89:4210:1)
    at invokeGuardedCallback (react-dom.development.js?ac89:4274:1)
    at beginWork$1 (react-dom.development.js?ac89:27405:1)
    at performUnitOfWork (react-dom.development.js?ac89:26513:1)
    at workLoopSync (react-dom.development.js?ac89:26422:1)
    at renderRootSync (react-dom.development.js?ac89:26390:1)
    at performConcurrentWorkOnRoot (react-dom.development.js?ac89:25694:1)
    at workLoop (scheduler.development.js?bcd2:266:1)
    at flushWork (scheduler.development.js?bcd2:239:1)
    at MessagePort.performWorkUntilDeadline (scheduler.development.js?bcd2:533:1)
throwOnHydrationMismatch @ react-dom.development.js?ac89:14388
tryToClaimNextHydratableInstance @ react-dom.development.js?ac89:14401
updateHostComponent$1 @ react-dom.development.js?ac89:20711
beginWork @ react-dom.development.js?ac89:22447
callCallback @ react-dom.development.js?ac89:4161
invokeGuardedCallbackDev @ react-dom.development.js?ac89:4210
invokeGuardedCallback @ react-dom.development.js?ac89:4274
beginWork$1 @ react-dom.development.js?ac89:27405
performUnitOfWork @ react-dom.development.js?ac89:26513
workLoopSync @ react-dom.development.js?ac89:26422
renderRootSync @ react-dom.development.js?ac89:26390
performConcurrentWorkOnRoot @ react-dom.development.js?ac89:25694
workLoop @ scheduler.development.js?bcd2:266
flushWork @ scheduler.development.js?bcd2:239
performWorkUntilDeadline @ scheduler.development.js?bcd2:533
next-dev.js?3515:25 Warning: An error occurred during hydration. The server HTML was replaced with client content in <div>.
window.console.error @ next-dev.js?3515:25
printWarning @ react-dom.development.js?ac89:86
error @ react-dom.development.js?ac89:60
errorHydratingContainer @ react-dom.development.js?ac89:11440
recoverFromConcurrentError @ react-dom.development.js?ac89:25802
performConcurrentWorkOnRoot @ react-dom.development.js?ac89:25706
workLoop @ scheduler.development.js?bcd2:266
flushWork @ scheduler.development.js?bcd2:239
performWorkUntilDeadline @ scheduler.development.js?bcd2:533
react-dom.development.js?ac89:14388 Uncaught Error: Hydration failed because the initial UI does not match what was rendered on the server.
    at throwOnHydrationMismatch (react-dom.development.js?ac89:14388:1)
    at tryToClaimNextHydratableInstance (react-dom.development.js?ac89:14401:1)
    at updateHostComponent$1 (react-dom.development.js?ac89:20711:1)
    at beginWork (react-dom.development.js?ac89:22447:1)
    at beginWork$1 (react-dom.development.js?ac89:27381:1)
    at performUnitOfWork (react-dom.development.js?ac89:26513:1)
    at workLoopSync (react-dom.development.js?ac89:26422:1)
    at renderRootSync (react-dom.development.js?ac89:26390:1)
    at performConcurrentWorkOnRoot (react-dom.development.js?ac89:25694:1)
    at workLoop (scheduler.development.js?bcd2:266:1)
    at flushWork (scheduler.development.js?bcd2:239:1)
    at MessagePort.performWorkUntilDeadline (scheduler.development.js?bcd2:533:1)
throwOnHydrationMismatch @ react-dom.development.js?ac89:14388
tryToClaimNextHydratableInstance @ react-dom.development.js?ac89:14401
updateHostComponent$1 @ react-dom.development.js?ac89:20711
beginWork @ react-dom.development.js?ac89:22447
beginWork$1 @ react-dom.development.js?ac89:27381
performUnitOfWork @ react-dom.development.js?ac89:26513
workLoopSync @ react-dom.development.js?ac89:26422
renderRootSync @ react-dom.development.js?ac89:26390
performConcurrentWorkOnRoot @ react-dom.development.js?ac89:25694
workLoop @ scheduler.development.js?bcd2:266
flushWork @ scheduler.development.js?bcd2:239
performWorkUntilDeadline @ scheduler.development.js?bcd2:533
react-dom.development.js?ac89:20658 Uncaught Error: There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.
    at updateHostRoot (react-dom.development.js?ac89:20658:1)
    at beginWork (react-dom.development.js?ac89:22444:1)
    at beginWork$1 (react-dom.development.js?ac89:27381:1)
    at performUnitOfWork (react-dom.development.js?ac89:26513:1)
    at workLoopSync (react-dom.development.js?ac89:26422:1)
    at renderRootSync (react-dom.development.js?ac89:26390:1)
    at recoverFromConcurrentError (react-dom.development.js?ac89:25806:1)
    at performConcurrentWorkOnRoot (react-dom.development.js?ac89:25706:1)
    at workLoop (scheduler.development.js?bcd2:266:1)
    at flushWork (scheduler.development.js?bcd2:239:1)
    at MessagePort.performWorkUntilDeadline (scheduler.development.js?bcd2:533:1)
updateHostRoot @ react-dom.development.js?ac89:20658
beginWork @ react-dom.development.js?ac89:22444
beginWork$1 @ react-dom.development.js?ac89:27381
performUnitOfWork @ react-dom.development.js?ac89:26513
workLoopSync @ react-dom.development.js?ac89:26422
renderRootSync @ react-dom.development.js?ac89:26390
recoverFromConcurrentError @ react-dom.development.js?ac89:25806
performConcurrentWorkOnRoot @ react-dom.development.js?ac89:25706
workLoop @ scheduler.development.js?bcd2:266
flushWork @ scheduler.development.js?bcd2:239
performWorkUntilDeadline @ scheduler.development.js?bcd2:533
astoilkov commented 2 years ago

Will it be hard for you if you send me a Git repo that replicates the problem?

nepomuc commented 2 years ago

@astoilkov Here you go: https://github.com/nepomuc/use-local-storage-state-issue-50-reproduced

Created this repo for reproducting this issue. I put some instructions for you into the README.md, so you can easily reproduce the hydration error.

Thanks in advance!

astoilkov commented 2 years ago

Thank you! I will take a look at this soon.

astoilkov commented 2 years ago

I found out what the problem is. As I suspected, SSR doesn't work in React 18. I've written about what are the steps for supporting React 18 here. However, this issue raises the priority of the task. When I switch to useSyncExternalStore() API the issue will be fixed automatically.

Thanks for the repo and detailed information. I will write here when I'm done with the React 18 support and this issue is resolved.

Miloshinjo commented 2 years ago

I have the same error and it's cause Im using React 18. I decided to wait for the new version, but in the meantime, I have got around the problem by doing useIsClient hook before my html renders. Since it's a side project and not in production yet, I'm not in a rush and workaround does me just fine.

import { useEffect, useState } from 'react';

/**
 * Let's us know if we are on the Client or the Server
 */
export default function useIsClient() {
  const [isClient, setClient] = useState(false);

  useEffect(() => {
    setClient(true);
  }, []);

  return isClient;
}
astoilkov commented 2 years ago

This issue is now fixed. The new version of use-local-storage-state supports React 18. You can upgrade.

I will close this issue but please let me know if you have a problem with the new version.

Thanks!

nepomuc commented 2 years ago

@astoilkov Works like a charm. Thanks a lot! 👍