skip to Main Content

I made this component representing a HH:MM clock.
It’s re-rendering every second. I would like to be re-rendered every minute.
A tooltip is attached to it. The tooltip cannot be shown because of the re-render.
How can I handle this situation ?

import TooltipComp from '@components/Shared/TooltipComp'
import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'

const Clock = () => {
    const [Time, setTime] = useState(new Date())
    const { t } = useTranslation();

    useEffect(() => {
        let now = new Date()
        while (now.getSeconds() !== 0) {
            const interval = window.setInterval(() => {now = new Date()}, 1000)
            return () => window.clearInterval(interval);
        }
        setTime(now);
    }, [])
    
    const formatTime = (time) => {
        return time < 10 ? `0${time}` : time
    }

    const date = Time.toLocaleDateString(t('locale'), {
        weekday: 'long',
        year: 'numeric',
        month: 'long',
        day: 'numeric'
    })

    return (
    <>
        <TooltipComp text={date}>
            <div style={{fontSize: "9px", fontFamily: "PX", cursor: "default"}}>
                {formatTime(Time.getHours())}
                &thinsp;:&thinsp;
                {formatTime(Time.getMinutes())}
            </div>
        </TooltipComp>
    </>
  )

  
}

export default Clock

I already tried using useMemo hooks, and multiple variations of the useEffect one.

2

Answers


  1. There should not be an issue with re-rendering every second. Infact in a clock like this that should be desirable. Have you checked if your clock is working?

      useEffect(() => {
            let now = new Date()
            while (now.getSeconds() !== 0) {
                const interval = window.setInterval(() => {now = new Date()}, 1000)
                return () => window.clearInterval(interval);
            }
            setTime(now);
        }, [])
    

    You seem to return out of your useEffect before you’ve set state. Which means your UI would not be receiving any state updates.

    useEffect(() => {
        // Adjust this to 60 * 1000 for 1 min (not required)
        const refreshTime = 1000;
        const interval = setInterval(() => {
          setTime(new Date())
        }, refreshTime)
    
        return () => {
          clearInterval(interval)
        }
      }, []);
    
    Login or Signup to reply.
  2. I tried to continue with your code, However, it found little difficult to understand. The while loop is something to be reviewed. Therefore reworked the implementation as below. Please see if it makes sense to you.

    The app will move along with the system clock as shown in the trial run output below :

    Browser display – the clock works in synch with the system clock

    Browser display - the clock works in synch with the system clock

    Coding highlights

    a. useEffect has been used to synch with the change in time.

    b. setTimeout has been used to synch the time with the first next minute from the start.

    Let us say the app has started at 14:20:20 HH:mm:ss, it means 20 seconds have already been elapsed from 20th minute of 14th hour. Therefore the initial time displayed by the app will be 14:20. Now there is 40 seconds to elapse to the next minute. It means by elapsing 40 seconds from the start time, there should be a state change triggered by the code. This code has been setup by the async function setTimeout which will run the given code only once. This is the reason for using a expression like 1000 * (60 – startTime.getSeconds()) to set its time.

    c. Once the app has been in synch with the system time by the step b, the subsequent state changes are required exactly in every one minute. This will be done repeatedly by the code given in setInterval function by firing in every minute. Please take note of the expression 1000 * 60 – one minute, used here.

    b. forceRender is the only state used in the app which is just to trigger a render, and its value has been set with a random number.

    App.js

    import { useEffect, useState } from 'react';
    
    const Clock = () => {
      const [forceRender, setforceRender] = useState();
      const startTime = new Date();
    
      useEffect(() => {
        setTimeout(() => {
          setforceRender(Math.random());
          setInterval(() => {
            setforceRender(Math.random());
          }, 1000 * 60);
        }, 1000 * (60 - startTime.getSeconds()));
      }, []);
    
      return (
        <>
          <br />
          <br />
          {`${startTime.getHours().toString()} : ${startTime
            .getMinutes()
            .toString()}`}
        </>
      );
    };
    
    export default Clock;
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search