Michael Lynch
Michael Lynch

Reputation: 3149

How can I conditionally render a React component if a sessionStorage variable does not exist in Next.js?

I have an <Authenticated> component that is used to render all of my authenticated routes. I want to prevent rendering the page until I have checked a token stored in sessionStorage.

'use client';

export const Authenticated = ({ children }) => {
  const token = typeof window !== 'undefined' ? sessionStorage.getItem('token') : null;

  if (!token) {
    return <></>;
  }

  return (
    <main>{children}</main>
  );
}

This works but I get this hydration error:

Uncaught Error: Hydration failed because the initial UI does not match what was rendered on the server.

Is there a better way to do this?

Upvotes: 0

Views: 80

Answers (1)

Ethan
Ethan

Reputation: 1637

According to the Next.js docs hydration errors can be caused by:

Using checks like typeof window !== 'undefined' in your rendering logic

That is what you are doing here:

const token = typeof window !== 'undefined' ? sessionStorage.getItem('token') : null;

Instead wrap your session handling logic into your useEffect hook. This will ensure that you only check for a token when the page has fully loaded and that window is available:

'use client';

import React, {useState, useEffect} from 'react';

export const Authenticated = ({ children }) => {
  const [token, setToken] = useState('');

  useEffect(() => {
     setToken(window.sessionStorage.getItem('token'))
  }, [])

  return (
    {token ? <main>{children}</main> : null}
  );
}

A few things to note about this approach:

  • We are using state to store the token. This is because token will be falsy before useEffect runs. This lets us update the state and re-render the UI once the token is retrieved

  • Building off of the last point we are using a ternary operator to determine whether to render something or not. The reason the previous approach might not have worked(even if you fixed the hydration error) is because of this logical error, your code says:

    • If we have a token (possibly before the page even loads) return a fragment
    • Otherwise return {children}

    The problem is the first condition will always be true right away then maybe false. However, currently you would just return before you met the true condition. But when using if/else logic inside on single return you can fix this issue

Upvotes: 1

Related Questions