Home > Software engineering >  state hooks show value one time and after that NaN and undefined
state hooks show value one time and after that NaN and undefined

Time:12-10

I am working on countdown app but when i start it it decrease by one and after that values of hour, minute and second shows as undefined, undefined and nan.

const [time, setTime] = useState({ 'hour': 0, 'minute': 0, 'second': 0 });
  const savedTime = useRef(null);
  const changeTime = (e) => {
    setTime({ ...time, [e.target.name]: e.target.value });
  };
  let updatedHour = time.hour, updatedMinute = time.minute, updatedSecond = time.second;
  const onStart = () => {
    savedTime.current = setInterval(()=> tick(), 1000);
  };

  useEffect(() => {
    onStart()
    return () => clearInterval(savedTime.current)
  }, [time])

this is my time state which have hour, min and sec.

function tick(){ 
    if (updatedHour === 0 && updatedMinute === 0 && updatedSecond === 0) {
       onReset()
      }  else if (updatedMinute === 0 && updatedSecond === 0) {
      setTime({updatedHour: updatedHour - 1, updatedMinute: 59, updatedSecond: 59})
    } else if (updatedSecond === 0 ) {
        setTime({updatedMinute: updatedMinute - 1, updatedSecond: 59})
    } else {   
        setTime({updatedHour: updatedHour, updatedMinute: updatedMinute,updatedSecond: updatedSecond - 1})
    }

};

this is tick function which will decrease time by 1 second.

 <div>
        <span>{updatedHour} :</span>
        <span>{updatedMinute} :</span>
        <span>{updatedSecond}</span>
      </div>
      <div>
        <input onChange={changeTime} name="hour" value={updatedHour} />
        <input onChange={changeTime} name="minute" value={updatedMinute} />
        <input onChange={changeTime} name="second" value={updatedSecond}/>
      </div>

here is inputs that will show data.

CodePudding user response:

Issue is in your tick() function you are setting

setTime({updatedHour: updatedHour - 1, updatedMinute: 59, updatedSecond: 59}) instead of setTime({hour: updatedHour - 1, minute: 59, second: 59})

here is a fully working example https://codesandbox.io/s/fervent-taussig-kf0x2?file=/src/App.js

import { useState, useEffect } from "react";
export default function App() {
  const [time, setTime] = useState({ hour: 0, minute: 0, second: 0 });
  const changeTime = (e) => {
    setTime({ ...time, [e.target.name]: e.target.value });
  };
  let updatedHour = time.hour,
    updatedMinute = time.minute,
    updatedSecond = time.second;

  useEffect(() => {
    let timer = setInterval(() => tick(), 1000);
    return () => clearInterval(timer);
  }, []);

  const tick = () => {
    if (updatedHour === 0 && updatedMinute === 0 && updatedSecond === 0) {
      // onReset();
      return;
    } else if (updatedMinute === 0 && updatedSecond === 0) {
      setTime({
        hour: updatedHour - 1,
        minute: 59,
        second: 59
      });
    } else if (updatedSecond === 0) {
      setTime({ minute: updatedMinute - 1, second: 59 });
    } else {
      setTime({
        hour: updatedHour,
        minute: updatedMinute,
        second: updatedSecond - 1
      });
    }
  };

  return (
    <>
      <div>
        <span>{updatedHour} :</span>
        <span>{updatedMinute} :</span>
        <span>{updatedSecond}</span>
      </div>
      <div>
        <input onChange={changeTime} name="hour" value={updatedHour} />
        <input onChange={changeTime} name="minute" value={updatedMinute} />
        <input onChange={changeTime} name="second" value={updatedSecond} />
      </div>
    </>
  );
}

CodePudding user response:

This happens because in setTime you set the fields updatedHour, updatedMinute and updatedSecond, instead of the original hour, minute, second. Try it this way:

function tick() {
    if (updatedHour === 0 && updatedMinute === 0 && updatedSecond === 0) {
      onReset();
    } else if (updatedMinute === 0 && updatedSecond === 0) {
      setTime((time) => ({
        hour: time.hour - 1,
        minute: 59,
        second: 59
      }));
    } else if (updatedSecond === 0) {
      setTime((time) => ({
        ...time,
        minute: time.minute - 1,
        second: 59
      }));
    } else {
      setTime((time) => ({
        ...time,
        second: time.second - 1
      }));
    }
  }

I used the callback function in setTime because it is considered a recommended practice. If you wish, you can use the normal setTime call, example:

setTime({
  ...time,
  minute: time.minute - 1,
  second: 59
});

And do not forget that setTime does not merge the passed state with the previous one, but replaces it.

  • Related