First of all, I know there are a lot of questions on this topic, and I've read through all the ones that I thought applied to my situation. This thread React component initialize state from props in particular seemed to be what I needed, but nothing mentioned here worked. Anyway, onto my code. It's just a simple countdown timer that takes in user input (in minutes) for its starting point:
class Timer extends Component {
constructor(props) {
super(props);
this.state = {
minutes: props.workPeriod,
seconds: 0
};
}
componentDidMount() {
setInterval(() => {
const {minutes, seconds} = this.state
console.log("minute state: ", minutes)
if(this.props.countdownHasStarted) {
if(seconds > 0) {
this.setState(({seconds}) => ({
seconds: seconds - 1
}))
}
if(seconds === 0) {
if(minutes === 0) {
clearInterval(this.myInterval)
} else {
this.setState(({minutes}) => ({
minutes: minutes - 1,
seconds: 59
}))
}
}
}
}, 1000)
}
...
const selector = formValueSelector('intervalSettings')
Timer = connect(state => {
const workPeriod = selector(state, 'workPeriod')
return {
workPeriod,
countdownHasStarted: state.countdownHasStarted
}
})(Timer)
Due to where everything's located on the component tree, I used Redux, so workPeriod comes from the Redux store, if that makes any difference. When I print out 'minutes' in the console, I get undefined, and sure enough when it's rendered, I just get NaN for the minutes. How do I get props.workPeriod into state so that it's defined and able to be manipulated?
I included how I got workPeriod from the Redux store just in case my woes have something to do with that, but {this.props.workPeriod} renders just fine, so I assume everything's good there.
Thanks in advance!
(edited to incorporate previous suggestions and questions)
This is happening because the redux store is initialized with an empty object. By the time the redux-form reducer initializes its own initialValues
, the <Timer />
component gets an undefined value of workPeriod
and kicks-off the setInterval(). Here is how I would solve this issue using React Hooks:
import React, { useState, useEffect } from "react";
import { connect } from "react-redux";
import { formValueSelector } from "redux-form";
let Timer = ({ workPeriod, countdownHasStarted }) => {
const [seconds, setSeconds] = useState(0);
const [minutes, setMinutes] = useState();
useEffect(() => {
// first initialize the minutes when the workPeriod is initialized
setMinutes(workPeriod);
}, [workPeriod]);
useEffect(() => {
let interval;
if (minutes) {
interval = setInterval(() => {
console.log("minute state: ", minutes);
if (countdownHasStarted) {
if (seconds > 0) {
setSeconds((sec) => sec - 1);
}
if (seconds === 0) {
if (minutes === 0) {
clearInterval(interval);
} else {
setMinutes((min) => min - 1);
setSeconds(59);
}
}
}
}, 1000);
}
return () => {
// cleanup function
clearInterval(interval);
};
}, [countdownHasStarted, minutes, seconds]);
return (
<div className="numbers">
<div className="box" id="minutes">
<p>{minutes}</p>
</div>
<div className="box" id="colon">
<p>:</p>
</div>
<div className="box" id="seconds">
<p>{seconds < 10 ? `0${seconds}` : seconds}</p>
</div>
</div>
);
};
Timer = connect((state) => {
const selector = formValueSelector("intervalSettings");
const workPeriod = selector(state, "workPeriod");
return {
workPeriod,
countdownHasStarted: state.countdownHasStarted,
};
})(Timer);
export default Timer;