Next.js Discord

Discord Forum

Hydration failed. Initial UI does not match what was rendered on the server.

Answered
Savannah posted this in #help-forum
Open in Discord
SavannahOP
I have a page that displays username. It retrieves it via localStorage on client. Here's how it is defined:

"use client"

import useAuth from "@/hooks/useAuth";

export default function Home() {
  const { user } = useAuth()

  return (
    <main>
      <p>
        {user?.username}
      </p>
    </main>
  )
}


I guess this is happening because on server there is no localStorage and it fetches user as null thus displaying no username, but when the UI gets rendered by client, it changes to username which causes an error. The problem is that idk how to fix it =)

I am setting the user right in the context provider via retrievePreviousUser function (bc it's already stored in localstorage)
function AuthProvider({ children }: {children: React.ReactNode}) {
  const [ user, setUser ] = useState<User | null>(retrievePreviousUser)

  return (
    <AuthContext.Provider value={{user, setUser}}>
        {children}
    </AuthContext.Provider>
  )
}
Answered by Ray
try this
function AuthProvider({ children }: {children: React.ReactNode}) {
  const [ user, setUser ] = useState<User | null>(null)

  useEffect(() => {
    setUser(retrievePreviousUser())
  }, [])

  return (
    <AuthContext.Provider value={{user, setUser}}>
        {children}
    </AuthContext.Provider>
  )
}
View full answer

14 Replies

SavannahOP
export function getOrCreateNewUser(username: string) {
    const user = localStorage.getItem(`profile-${username}`)

    let newUser
    if (user) {
        newUser = JSON.parse(user, dateReviver)
    } else {
        newUser = createNewUser(username)
        localStorage.setItem(`profile-${username}`, JSON.stringify(newUser))
    }

    const prevMeta = getMeta()
    prevMeta.currentUser = newUser.username
    localStorage.setItem("meta", JSON.stringify(prevMeta))
    return newUser
}

function getMeta(): MetaScheme {
    const data = typeof window !== "undefined" ? localStorage.getItem("meta") : null

    if (data) {
        return JSON.parse(data)
    }

    return DefaultMeta
}

export function retrievePreviousUser(): User | null {
    const prevUser = getMeta().currentUser

    if(!prevUser) {
        return null
    }

    // In this context, must never create a user
    return getOrCreateNewUser(prevUser)
}
Also included it's dependencies
Answer
SavannahOP
Yes, this works, but there is a small delay. I guess I cannot get rid of it?
yeah because it need to access the localStorage
SavannahOP
=(
so its better to use cookies
or render some loading ui
@Ray or render some loading ui
SavannahOP
Won't it give the same hydration error?
I don't think so?
"use client"

import useAuth from "@/hooks/useAuth";

export default function Home() {
  const { user, loading } = useAuth()

  if (loading) {
    return <div>loading...</div>
  }

  return (
    <main>
      <p>
        {user?.username}
      </p>
    </main>
  )
}

function AuthProvider({ children }: {children: React.ReactNode}) {
  const [ user, setUser ] = useState<User | null>(null)
  const [loading,setLoading] = useState<boolean>(true)

  useEffect(() => {
    setUser(retrievePreviousUser())
    setLoading(false)
  }, [])

  return (
    <AuthContext.Provider value={{user, setUser, loading}}>
        {children}
    </AuthContext.Provider>
  )
}
SavannahOP
I see.. Yep, that probably would work
Thanks for help!
you're welcome