skip to Main Content

I’m learning ReactJS and my first project is online alarm.
So I decided to check out my clock if it has some bugs and… It has.

I checked some console logs of rendering function and memory using with F12 in Chrome.


You can see that every second I have more and more of rerenders.

Absurdly amount.


import React, { useState, useEffect } from "react";
import classes from "./Clock.module.css";
import { useMemo } from "react";

const ClockCurrent = (props) => {
    //*TODO:
    //!If put console.log(time) you can see that useState is overused
    //! and PC's memory overfilling, because
    //! entity of rerenders getting bigger every second like
    //! geometrical progression*

    const [time, setTime] = useState(new Date());
    const refreshClock = () => {
        setTime(new Date());
    };

    useEffect(() => {
        setInterval(refreshClock, 1000);
        console.log(time);
    }, [time]);

    return (
        <div className={classes.Clock}>
            <h2>
                {useMemo(
                    () =>
                        time.toLocaleTimeString("ru", {
                            timeZone: "Europe/Moscow",
                        }),
                    [time]
                )}
            </h2>
        </div>
    );
};

export default ClockCurrent;


In the code above you can see how I used useState() and useEffect() func.
useMemo didn’t help me at all, by the way.
Don’t think my realisation is right but it is what it is and hope I’ll fix this bug in memory using.

I have almost the same (differs only in .toLocateTimeString properties) code to getting the date and it has exactly same problem.


What I tried to do to solve it:

  1. Include useMemo() to my code
  2. Don’t use useState and useEffect
  3. Try lots of ways to relize the useState method

All of cases didn’t work at all excepting the 2nd, when I didn’t get tracebacks .

4

Answers


  1. Chosen as BEST ANSWER

    Task closed

    So, thanks a lot! for helping with this issue, guys, you are the best one. You was really right says that useEffect triggers setInterval every time the time changes.

    So the solution is:

    To the useEffect function adding a return an anonymic func with clearInterval method:

    useEffect(() => {
            const intervalId = setInterval(refreshClock, 1000);
    
            return () => {
                clearInterval(intervalId); // Clear the interval on component unmount
            };
        }, []);
    

    Thanks a lot again not only for solution but for explaining how useState and useEffect works as well. Now I understand it a bit deeply.

    I did the same to gettingDate function.

    If earlier I got overfilled memory, now it became cleared every ~7-10 seconds.


  2. Because the logic in useEffect does three things:

    1. It executes whenever time changes:

      }, [time]);
      
    2. It creates a new interval:

      setInterval(refreshClock, 1000);
      
    3. And in that interval it updates time:

      setTime(new Date());
      

    If you only want the useEffect to run once when the component loads (so it only creates one interval), use an empty dependency array. Additionally, if a useEffect produces side-effects like timeouts or intervals, it should also clean those up by returning a clean-up function. For example:

    useEffect(() => {
      const i = setInterval(refreshClock, 1000);
      return () => clearInterval(i);
    }, []);
    

    If you also want to output the value of time whenever it changes, that’s an entirely different effect:

    useEffect(() => {
      console.log(time);
    }, [time]);
    

    Though you could also just put that directly in the component:

    console.log(time);
    

    Since every time state is updated the component will re-render anyway.

    Login or Signup to reply.
  3. you have defined [time] as a dependency in your useEffect. This means that this hook will always be called when the time variable is changed. This means that you define a new interval every second, which will cause your component to be rendered again.

    You have to make sure that the interval is defined exactly once when your component is mounted and also released again when your component is dismounted.

    import React, { useState, useEffect } from "react";
    import classes from "./Clock.module.css";
    
    const ClockCurrent = (props) => {
        const [time, setTime] = useState(new Date());
    
        useEffect(() => {
            const intervalId = setInterval(() => {
                setTime(new Date());
            }, 1000);
    
            // Clean up function
            return () => {
                clearInterval(intervalId);
            };
        }, []); // Empty dependency array ensures this runs once on mount and clean up on unmount
    
        return (
            <div className={classes.Clock}>
                <h2>
                    {time.toLocaleTimeString("ru", {
                        timeZone: "Europe/Moscow",
                    })}
                </h2>
            </div>
        );
    };
    
    export default ClockCurrent;
    
    Login or Signup to reply.
  4. In the provided code, the useState is updating because it’s part of the component’s state and is being used in the useEffect hook, which triggers every second due to the interval set by setInterval. The issue is that you are creating a new interval on every render, which leads to multiple intervals running simultaneously, causing excessive updates and memory consumption.

    To fix this issue, you should clear the interval when the component unmounts to avoid multiple intervals running at the same time. You can do this by returning a cleanup function from the useEffect hook.

    Try this code:

    import React, { useState, useEffect } from "react";
    import classes from "./Clock.module.css";
    import { useMemo } from "react";
    
    const ClockCurrent = (props) => {
      const [time, setTime] = useState(new Date());
    
      const refreshClock = () => {
        setTime(new Date());
      };
    
      useEffect(() => {
        const intervalId = setInterval(refreshClock, 1000);
    
        return () => {
          clearInterval(intervalId); // Clear the interval on component unmount
        };
      }, []); // Empty dependency array, so the effect runs only once on mount
    
      return (
        <div className={classes.Clock}>
          <h2>
            {useMemo(
              () =>
                time.toLocaleTimeString("ru", {
                  timeZone: "Europe/Moscow",
                }),
              [time]
            )}
          </h2>
        </div>
      );
    };
    
    export default ClockCurrent;
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search