简体   繁体   中英

How to make a input uncontrolled in react

My goal is to make a countdown clock with custom inputs. Current it works fine, but the requirement is to make the input fields independent to the timer. That is, currently whatever is entered into the input field, it also changes the timer. The timer should change and start only when start button is clicked. But using onChange in input, changes the timer on the go.

Codesandbox link:https://codesandbox.io/s/lively-grass-6o50xx?file=/src/Timer.js

Code:

const START_DERATION = 10;
function Timer() {
  const [currentMinutes, setMinutes] = useState("00");
  const [currentSeconds, setSeconds] = useState("00");
  const [isStop, setIsStop] = useState(false);
  const [duration, setDuration] = useState(START_DERATION);
  const [isRunning, setIsRunning] = useState(false);

  const startHandler = async () => {
    setDuration(
      parseInt(currentSeconds, 10) + 60 * parseInt(currentMinutes, 10)
    );
    setIsRunning(true);
  };
  const stopHandler = () => {
    setIsStop(true);
    setIsRunning(false);
  };
  const resetHandler = () => {
    setMinutes("00");
    setSeconds("00");
    setIsRunning(false);
    setIsStop(false);
    setDuration(START_DERATION);
  };

  const resumeHandler = () => {
    let newDuration =
      parseInt(currentMinutes, 10) * 60 + parseInt(currentSeconds, 10);
    setDuration(newDuration);

    setIsRunning(true);
    setIsStop(false);
  };

  useEffect(() => {
    if (isRunning === true) {
      let timer = duration;
      var minutes, seconds;
      const interval = setInterval(function () {
        if (--timer <= 0) {
          resetHandler();
        } else {
          minutes = parseInt(timer / 60, 10);
          seconds = parseInt(timer % 60, 10);

          minutes = minutes < 10 ? "0" + minutes : minutes;
          seconds = seconds < 10 ? "0" + seconds : seconds;

          setMinutes(minutes);
          setSeconds(seconds);
        }
      }, 1000);
      return () => clearInterval(interval);
    }
  }, [isRunning]);

  return (
    <div>
      <span style={{ display: "flex" }}>
        <input
          type="number"
          onChange={(e) => setMinutes(e.target.value + "")}
        />
        <p>Minutes</p>
      </span>
      <span style={{ display: "flex" }}>
        <input
          type="number"
          onChange={(e) => setSeconds(e.target.value + "")}
        />
        <p>Seconds</p>
      </span>
      <button onClick={startHandler}>Start</button>
      <button
        onClick={isStop ? resumeHandler : stopHandler}
        disabled={!isRunning && !isStop}
      >
        Pause/Resume
      </button>
      <button onClick={resetHandler} disabled={!isRunning && !isStop}>
        Reset
      </button>
      <p>
        {currentMinutes}:{currentSeconds}
      </p>
    </div>
  );
}

Instead of making the inputs uncontrolled, you could always conditionally render the timer values. It would look something like:

<p>
  {isRunning || isStop
    ? <>{currentMinutes}:{currentSeconds}</>
    : '00:00'
  }
</p>

This just checks if the timer is currently running or paused and displays the input values. If it isn't running and isn't paused, it just displays 00:00.

What if you create new state variable disabledInputs and everytime the timer starts, the inputs are disabled so the user can't enter anything? When the timer stops, enable them again.

function Timer() {
  
  const [disabledInputs, setDisabledInputs] = useState(false)

  const startHandler = async () => {
  
    setDuration(
      parseInt(currentSeconds, 10) + 60 * parseInt(currentMinutes, 10)
    );
    
    setIsRunning(true);
    setDisabledInputs(true)
  };
  const stopHandler = () => {
    // stop timer
    setIsStop(true);
    setIsRunning(false);
  };
  const resetHandler = () => {
    setMinutes("00");
    setSeconds("00");
    setIsRunning(false);
    setIsStop(false);
    setDisabledInputs(false)
    setDuration(START_DERATION);
  };

  const resumeHandler = () => {
    let newDuration =
      parseInt(currentMinutes, 10) * 60 + parseInt(currentSeconds, 10);
    setDuration(newDuration);

    setIsRunning(true);
    setIsStop(false);
  };



  useEffect(() => {
    if (isRunning === true) {
      let timer = duration;
      var minutes, seconds;
      const interval = setInterval(function () {
        if (--timer <= 0) {
          resetHandler();
        } else {
          minutes = parseInt(timer / 60, 10);
          seconds = parseInt(timer % 60, 10);

          minutes = minutes < 10 ? "0" + minutes : minutes;
          seconds = seconds < 10 ? "0" + seconds : seconds;

          setMinutes(minutes);
          setSeconds(seconds);
        }
      }, 1000);
      return () => clearInterval(interval);
    }
  }, [isRunning]);

  

  return (
    <div>
      <span style={{ display: "flex" }}>
        <input
        disabled={disabledInputs}
          type="number"
          onChange={(e) => setMinutes(e.target.value + "")}
        />
        <p>Minutes</p>
      </span>
      <span style={{ display: "flex" }}>
        <input disabled={disabledInputs}
          type="number"
          onChange={(e) => setSeconds(e.target.value + "")}
        />
        <p>Seconds</p>
      </span>
      {/* <button onClick={formatTime}>Format</button> */}
      <button onClick={startHandler}>Start</button>
      <button
        onClick={isStop ? resumeHandler : stopHandler}
        disabled={!isRunning && !isStop}
      >
        Pause/Resume
      </button>
      <button onClick={resetHandler} disabled={!isRunning && !isStop}>
        Reset
      </button>
      <p>
        {isRunning || isStop ? (
          <>
            {currentMinutes}:{currentSeconds}
          </>
        ) : (
          "00:00"
        )}
      </p>
    </div>
  );
}

export default Timer;

Actually you can use the same isRunning state to disable them since they change together every time but disabledInputs is more specific and clear.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM