Skip to content

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

Closed

Description

@thorsten-stripe

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

Activity

  1. added
    bugSomething isn't working or fixes something that isn't working
    on Jun 12, 2020
  2. thorsten-stripe commented on Jun 12, 2020

    @thorsten-stripe
    CollaboratorAuthor
  3. thorsten-stripe commented on Jun 13, 2020

    @thorsten-stripe
    CollaboratorAuthor

    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.

  4. thorsten-stripe commented on Jun 13, 2020

    @thorsten-stripe
    CollaboratorAuthor
  5. andria-dev commented on Jun 14, 2020

    @andria-dev
    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?

  6. andria-dev commented on Jun 14, 2020

    @andria-dev
    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.

  7. thorsten-stripe commented on Jun 15, 2020

    @thorsten-stripe
    CollaboratorAuthor

    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 :(

  8. dayhaysoos commented on Jun 22, 2020

    @dayhaysoos
    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?

  9. thorsten-stripe commented on Jun 24, 2020

    @thorsten-stripe
    CollaboratorAuthor

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

  10. andria-dev commented on Jun 24, 2020

    @andria-dev
    Collaborator

    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.

  11. dayhaysoos commented on Sep 23, 2020

    @dayhaysoos
    Owner

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

  12. changed the title [-][Next.js] CartCount throws error on reload[/-] [+]Usage of cartCount causes an SSR mismatch error in Next.js[/+] on Feb 16, 2021
  13. andria-dev commented on Feb 16, 2021

    @andria-dev
    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".

  14. added
    on holdDepends on another issue to be fixed first.
    on Feb 16, 2021
  15. benwwalker commented on Feb 19, 2021

    @benwwalker

    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>

  16. andria-dev commented on Feb 19, 2021

    @andria-dev
    Collaborator

    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.

  17. dayhaysoos commented on Jul 4, 2021

    @dayhaysoos
    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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working or fixes something that isn't workingon holdDepends on another issue to be fixed first.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions