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

Usage of cartCount causes an SSR mismatch error in Next.js #122

Closed
thorsten-stripe opened this issue Jun 12, 2020 · 14 comments
Closed

Usage of cartCount causes an SSR mismatch error in Next.js #122

thorsten-stripe opened this issue Jun 12, 2020 · 14 comments
Labels
bug Something isn't working or fixes something that isn't working on hold Depends on another issue to be fixed first.

Comments

@thorsten-stripe
Copy link
Collaborator

thorsten-stripe commented Jun 12, 2020

I'm not sure why this is happening:
image

cartCount renders correctly in the <p> tag but for setting the disabled value on the button it doesn't: https://github.com/thorsten-stripe/next.js/blob/thor/stripe/add-use-shopping-cart-example/examples/with-stripe-typescript/components/CartSummary.tsx#L45

@thorsten-stripe thorsten-stripe added the bug Something isn't working or fixes something that isn't working label Jun 12, 2020
@thorsten-stripe
Copy link
Collaborator Author

This sounds similar: https://dev.to/jaklaudiusz/next-js-persistent-state-with-react-hooks-and-localstorage-how-to-make-it-work-3al6 but not sure how we can bake that into the lib.

@thorsten-stripe
Copy link
Collaborator Author

I did some digging and traced it down to the react-storage-hooks module: soyguijarro/react-storage-hooks#17 . Maybe they have seen that before and have an idea.

@thorsten-stripe
Copy link
Collaborator Author

@andria-dev
Copy link
Collaborator

So @thorsten-stripe the official way to fix it would be to modify react-storage-hooks to not load from LocalStorage on the initial client-side render, but instead right afterward right?

@andria-dev
Copy link
Collaborator

Oh nvm looks like you're really on top of all this and you've already put out a PR for this. Much appreciated.

@thorsten-stripe
Copy link
Collaborator Author

So @thorsten-stripe the official way to fix it would be to modify react-storage-hooks to not load from LocalStorage on the initial client-side render, but instead right afterward right?

@ChrisBrownie55 Yes, I believe that is the only way to avoid this discrepancy, however it's not great, because you have an initial render with the default state and then an immediate re-render with the localStorage state, which can result in a flicker when the page loads.

One potential solution would be to initially return null until we had the chance to check the localStorage, that way you can employ conditional rendering to avoid the "flicker", but none of this feels great for non-SSR applications :(

@dayhaysoos
Copy link
Owner

@thorsten-stripe it looks like this has been fixed here:

soyguijarro/react-storage-hooks#8

You think you can try testing the upgraded version of react-storage-hooks and see if the bug is gone?

@thorsten-stripe
Copy link
Collaborator Author

Unfortunately it didn't fix the hydration warning issue, see soyguijarro/react-storage-hooks#18 (comment)

@andria-dev
Copy link
Collaborator

andria-dev commented Jun 24, 2020

So what I'm seeing here is that we need to display the default (a.k.a. empty) values for data stored in local storage and provide a status indicator for the data as well.

I think this could look something like the following:

export function useLocalStorageReducer(key, reducer, initialState) {
  const [state, dispatch] = useReducer((state, action) => {
    switch (action.type) {
      case 'value updated':
        return { status: 'loaded', value: action.storageValue }
      case 'loading started':
        return { status: 'loading', value: action.initialState }
      default:
        return state
    }
  })

  const dummyStorage = {/* ... */}
  const storageValue = useStorageReducer(/* same stuff but make initial state: */ null)
  
  useEffect(() => {
    if (storageValue === null)
      dispatch({ type: 'loading started', initialState })
    else
      dispatch({ type: 'value updated', storageValue })
  }, [initialState, storageValue])
}

This would also make it so that when isClient is false it will always be status: 'loading'. Although, I don't think this accounts for window.localStorage being inaccessible, like if it's disabled, as we currently aren't handling that anyway.

@dayhaysoos
Copy link
Owner

@thorsten-stripe was this taken care of? Do we need to update this lib?

@andria-dev andria-dev changed the title [Next.js] CartCount throws error on reload Usage of cartCount causes an SSR mismatch error in Next.js Feb 16, 2021
@andria-dev
Copy link
Collaborator

It looks like setting suppressHydrationWarning is the only option until soyguijarro/react-storage-hooks#20 is merged. Once it is, we'll need to update our dependency. For now, I'll just tag this as "On Hold".

@andria-dev andria-dev added the on hold Depends on another issue to be fixed first. label Feb 16, 2021
@benwwalker
Copy link

benwwalker commented Feb 19, 2021

Fwiw I've been running into this whilst using material-ui badges too - things like

{cartCount > 0 ?
   <IconButton href="/cart">
      <Badge badgeContent={cartCount || 0} color="secondary" suppressHydrationWarning>
         <ShoppingCartOutlined />
      </Badge>
   </IconButton>
: <></>
}

which not only triggers the hydration warning, but also a mismatched className on the Badge being invisible (server side) or not (client side).

My only solution, for now, has been to go with the premise that the shopping cart interaction will be client-side only, and wrap any use of cartCount in <NoSSR>

@andria-dev
Copy link
Collaborator

andria-dev commented Feb 19, 2021

I'd say that that's a fair assumption as all of the cart information has to be loaded from either LocalStorage or with loadCart. However, @benwwalker, I feel like the hydration warning is triggered in your above example because you're still conditionally rendering different values to the DOM based on cartCount. You might try something like:

// "hidden" would be like "display: none" or something
<IconButton href="/cart" className={cartCount > 0 ? '' : 'hidden'} suppressHydrationWarning>
  <Badge badgeContent={cartCount || 0} color="secondary">
    <ShoppingCartOutlined />
  </Badge>
</IconButton>

If you still have the same issue with suppressHydrationWarning then <NoSSR> might be the only way at the time.

Other than that, we're still just waiting on soyguijarro/react-storage-hooks#20 to be merged to definitively solve this issue.

@dayhaysoos
Copy link
Owner

Don't think this is an issue anymore, let me know if I'm wrong tho

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working or fixes something that isn't working on hold Depends on another issue to be fixed first.
Projects
None yet
Development

No branches or pull requests

4 participants