in43sh
in43sh

Reputation: 933

Why does Next.js give error "Unhandled Runtime Error Error: Rendered more hooks than during the previous render."

I have a client component in my Nextjs application that is checking time spent in the app. Here is the code:

'use client';

import React, { useEffect, useState } from 'react';
import { useSession } from 'next-auth/react';

const TimeTracking: React.FC = () => {
  const { data: session, status } = useSession();

  if (status === 'loading') {
    return <div>Loading...</div>;
  }

  if (!session) {
    return <div>No session data</div>;
  }

  const [timeSpent, setTimeSpent] = useState<number>(0);

  const saveToLocalStorage = (value: number) => {
    if (typeof window !== 'undefined') {
      const todayDateString = new Date().toLocaleDateString();
      localStorage.setItem('date', todayDateString);
      localStorage.setItem('timeSpent', value.toString());
    }
  };

  useEffect(() => {
    let initialTimeSpent = 0;
    const todayDateString = new Date().toLocaleDateString();

    if (typeof window !== 'undefined' && session) {
      if (localStorage.getItem('date') === todayDateString) {
        initialTimeSpent = parseInt(localStorage.getItem('timeSpent') || '0');
      }
      setTimeSpent(initialTimeSpent);

      const updateTimeSpent = () => {
        setTimeSpent((prevTimeSpent) => {
          const updatedTimeSpent = prevTimeSpent + 1;
          saveToLocalStorage(updatedTimeSpent);
          return updatedTimeSpent;
        });
      };

      const timer = setInterval(updateTimeSpent, 1000);

      const handleBeforeUnload = (e: BeforeUnloadEvent) => {
        const todayDateString = new Date().toLocaleDateString();
        const timetracker = {
          timeSpent: localStorage.getItem('timeSpent'),
          date: todayDateString,
        };
        const requestBody = JSON.stringify({ timetracker });
        navigator.sendBeacon(`/api/user/${session?.user?._id}`, requestBody);
      };

      window.addEventListener('beforeunload', handleBeforeUnload);
      return () => {
        clearInterval(timer);
        window.removeEventListener('beforeunload', handleBeforeUnload);
      };
    }
  }, [session]);

  return (
    <div>
      <h3>Total Time Spent: {timeSpent} minutes</h3>
      <p>Tracking time spent in the app...</p>
    </div>
  );
};

export default TimeTracking;

It gives me an error "Unhandled Runtime Error Error: Rendered more hooks than during the previous render." But if I remove if statements that are checking status in the beginning, the error will go away. Can someone explain why?

Here's the screenshot:

enter image description here

Upvotes: 0

Views: 628

Answers (1)

Stitt
Stitt

Reputation: 494

As the error states, you are rendering more hooks on one render than the previous, because you are failing to reach the call to one of your hooks (line 17), due to your status-checking statements.

Simply move the code currently on line 17 above the if-statements and you will not encounter any more issues.

const TimeTracking: React.FC = () => {
    const { data: session, status } = useSession();
    const [timeSpent, setTimeSpent] = useState<number>(0);

    if (status === 'loading') {
        return <div>Loading...</div>;
    }

    if (!session) {
        return <div>No session data</div>;
    }
    .
    .
    .
}

Upvotes: 1

Related Questions