Nextjs - getting cookies value on _app init

Viewed 3529

I need to get the cookies value on the first render. I get those in _app.tsx. Everything looks fine (render correctly the html) but I get the server/client mismatch warning because at the first render on Server, cookies are undefined and the value fall back to default value which is 0. On hydration, the value is picked from cookies and is displayed correctly.

Could someone explain to me why is a problem that on the server the value is the default value (therefor why I get this warning) and what would be a better way to write this code?

Here my _app.tsx

import React, { useState, useEffect } from 'react'
import type { AppProps } from 'next/app'
import { UserContext } from '../context/UserContext'
require('es6-promise').polyfill()

let cookieHelper
if (typeof window !== 'undefined') {
  cookieHelper = require( '../helpers/_cookies' ) // This is a file written by us where we export const and get/set cookies func 
}

function ElliotApp ({ Component, pageProps }: AppProps) {
  useEffect(() => {
    import('../helpers/_cookies')
  }, [])
  
  const searchesFromCookies = cookieHelper?.get(cookieHelper?.COOKIE_NAME_SEARCH_COUNT) // this value is a string like '3'
  const userState = { 
    numOfSearches: searchesFromCookies || 0 
  }
  const [userContext, setUserContext] = useState(userState)

  useEffect(() => {
    cookieHelper?.set(cookieHelper?.COOKIE_NAME_SEARCH_COUNT, userContext.numOfSearches)
  }, [userContext])

  return (
    <UserContext.Provider value={[userContext, setUserContext]}>
      <Component {...pageProps} />
    </UserContext.Provider>
  )
}

export default ElliotApp

many thanks!

1 Answers

Could someone explain to me why is a problem that on the server the value is the default value

Probably because your cookieHelper is just reading cookies from document.cookie and there is no such thing on the server.

If you want to get cookie with SSR you could use getInitialProps:

function parseCookies(req) {
  // cookie.parse is some function that accepts cookie string and return parsed object
  return cookie.parse(req ? req.headers.cookie || "" : document.cookie)
}


ElliotApp.getInitialProps = async ({ req }) => {
  const cookies = parseCookies(req)

  return {
    searchesFromCookies: cookies[COOKIE_NAME_SEARCH_COUNT]
  }
}

and then do something with them in your App component:

function ElliotApp ({ Component, pageProps, searchesFromCookies }: AppProps) {
  const userState = { 
    numOfSearches: searchesFromCookies || 0 
  }
  const [userContext, setUserContext] = useState(userState)

  // do whatever ...
}

EDIT:

In case you are fine with default value on the server then you just need to do everything inside useEffect hook (it wont run on the server):

function ElliotApp ({ Component, pageProps }: AppProps) {
  const userState = { 
    numOfSearches: 0 
  }
  const [userContext, setUserContext] = useState(userState)

  useEffect(() => {
    setUserContext({
      numOfSearches: cookieHelper.get(cookieHelper.COOKIE_NAME_SEARCH_COUNT)
    });
  }, []);

  useEffect(() => {
    cookieHelper.set(cookieHelper.COOKIE_NAME_SEARCH_COUNT, userContext.numOfSearches)
  }, [userContext])

  return (
    <UserContext.Provider value={[userContext, setUserContext]}>
      <Component {...pageProps} />
    </UserContext.Provider>
  )
}
Related