I'm new to React and building a project that requires dynamically setting a countdown timer from a parent component. I found a react countdown timer online that uses a hook, but I'm not too familiar hooks yet.
Toward the bottom of my code, you can see a parent class where I'm passing 'cycleTimeSelected' to the Countdown component/hook. It works correctly up to that point.
But I'm not having success getting it to update the timer correctly and dynamically. timeRemaining in React.useState() is the variable I need to update. It doesn't work if I use props.cycleTimeSelected directly. I think I understand why that is, so I tried to use componentWillRecieveProps to set the state, but that's not working.
I'm think I'm confused about React.useState and how that relates to setState for one thing. Can anyone spot my problem here?
import React, { Component } from 'react'
import { PausePresentation, SkipNext, Stop, PlayCircleOutline} from '@material-ui/icons';
import './Timer.css'
function Countdown(props) {
const [timer, setTimer] = React.useState({
name: 'timer',
isPaused: true,
time: 100,
timeRemaining: props.cycleTimeSelected,
timerHandler: null
})
//const componentWillReceiveProps = nextProps => {
//this.setState({ timeRemaining: nextProps.cycleTimeSelected });
//}
const handleTimeChange = e => {
setTimer({
...timer,
time: props.cycleTimeSelected,
timeRemaining: Number(e.target.value),
})
}
React.useEffect(() => {
if (timer.timeRemaining === 0) {
clearInterval(timer.timerHandler)
}
}, [timer.timeRemaining, timer.timerHandler])
const updateTimeRemaining = e => {
setTimer(prev => {
return { ...prev, timeRemaining: prev.timeRemaining - 1 }
})
}
const handleStart = e => {
const handle = setInterval(updateTimeRemaining, 1000);
setTimer({ ...timer, isPaused: false, timerHandler: handle })
}
const handlePause = e => {
clearInterval(timer.timerHandler)
setTimer({ ...timer, isPaused: true })
}
return <React.Fragment>
{/* { <input value={props.cycleTimeSelected} type="" onChange={handleTimeChange} /> } */}
{timer.name && timer.time && <div className={timer.timeRemaining === 0 ? 'time-out':''}>
{timer.isPaused && <div className="floatLeft"><i className="material-icons pause"><PlayCircleOutline onClick={handleStart}></PlayCircleOutline></i></div>}
{!timer.isPaused && <div className="floatLeft"><i className="material-icons pause"><PausePresentation onClick={handlePause}></PausePresentation></i></div>}
{`Remaining: ${timer.timeRemaining}`}
</div>}
</React.Fragment>
}
class Timer extends React.Component {
render(){
return(
<>
<div className="floatLeft"><i className="material-icons bottom-toolbar stop"><Stop onClick={this.clickStop}></Stop></i></div>
<div className="floatLeft"><i className="material-icons bottom-toolbar skip_next"><SkipNext onClick={this.clickSkip}></SkipNext></i></div>
<div className="floatLeft"><div id="timer"><Countdown cycleTimeSelected={this.props.cycleTimeSelected}></Countdown></div></div>
</>
);
}
}
If you want
timer.timeRemaining
inCountdown
to update via props then you should implement an effect with a dependency onprops.cycleTimeSelected
.useEffect
is nearly the functional component equivalent to a class-based component'scomponentDidMount
,componentDidUpdate
, andcomponentWillUnmount
lifecycle functions. We're interested in the "update" lifecycle.