Reputation: 4588
Complete working example at jsbin: http://jsbin.com/tepedepozi/1/edit?console,output
_____________________________________________________________--
I have a component named App and the full returned JSX is here:
<main style = {mainContainer}>
<Loader/>
<section style={goldPanel}>
<TimeDisplay isPlaying = {this.state.isPlaying}/>
<div className="buttonContainer">
<Button buttonText = {this.state.recordButtonText} onClick={this.toggleRecording}/>
<Button buttonText = {this.state.playButtonText} onClick={this.toggleSongPlaying} />
</div>
</section>
</main>
Notice there are two buttons: a record button and a play button
When then the user clicks the play button:
<Button buttonText = {this.state.playButtonText} onClick={this.toggleSongPlaying} />
a method is invoked that changes this.state.isPlaying
to true
:
If the user clicks th record button it does not change this.state.isPlaying
at all - ever ! This is how I programmed it and it is the behavior I want !
In the TimeDisplay
component I pass the state of this.state.isPlaying
as a prop.
<TimeDisplay isPlaying = {this.state.isPlaying}/>
The problem is that I want to program TimeDisplay to behave in ways that are based on this state, and the TimeDisplay component should only respond to the state change based on users clicking:
<Button buttonText = {this.state.playButtonText} onClick={this.toggleSongPlaying} />
However, when the user clicks the play button and then the record button the record button forces a rerender on the TimeDisplay component. I demonstrate the problem by placing a setInterval in the startCount()
method code below.
The TimeDisplay component:
class TimeDisplay extends React.Component {
constructor(props) {
super(props)
}
startCounter(){
if(this.props.isPlaying){
console.log("counter working"); // runs no matter what button is clicked after play button
setInterval(()=>{
console.log("Multiple setIntervals get invoked when user clicks record...not good");
},1000)
}
}
render() {
this.startCounter() //______Due to rerender. How to fix ?
return (
<div>
<p><span id="seconds">00</span>:<span id="tens">00</span></p>
</div>
)
}
}
Upvotes: 1
Views: 1100
Reputation: 281892
You need to write this.startCounter()
function in your componentDidMount method and not in the render since the setInterval will do the job for you and you only have to call the setInterval once and not on every render. Now that isPlaying is received from props, you need to also update your child counter. You can do that in componentDidUpdate like
class TimeDisplay extends React.Component {
constructor(props) {
super(props)
this.interval = null;
}
componentDidMount() {
this.startCounter();
}
componentDidUpdate(prevProps) {
if(prevProps.isPlaying !== this.props.isPlaying) {
this.startCounter();
}
}
startCounter() {
if (this.props.isPlaying) {
this.interval = setInterval(() => {
console.log("Multiple setIntervals get invoked when user clicks record...not good");
}, 1000);
}else{
clearInterval(this.interval);
}
}
render() {
return (
<div>
<p><span id="seconds">00</span>:<span id="tens">00</span></p>
</div>
)
}
}
class Button extends React.Component {
constructor(props) {
super(props)
}
render() {
return (<button onClick={this.props.onClick}>{this.props.buttonText}</button>)
}
}
P.S. I have used
componentDidUpdate
instead ofcomponentWillReceiveProps
because React has proposed that they will renamecomponentWillReceiveProps
fromv16.3.0
onwards and remove it from v17. Check this conversation
Upvotes: 1
Reputation: 12103
You need to cancel the interval before triggering new. Also @shubham suggested do call startCounter
method like he suggested.
constructor(props) {
super(props)
this.timeInterval = null;
}
}
componentWillUnmount(){
if(this.timeInterval != null){
clearInterval(this.timeInterval)// Clear interval on page unmount
}
}
startCounter(){
if(this.timeInterval != null){
clearInterval(this.timeInterval)// Cancel interval if it is being executing.
}
if(this.props.isPlaying){
console.log("counter working"); // runs no matter what button is clicked after play button
this.timeInterval = setInterval(()=>{
console.log("Multiple setIntervals get invoked when user clicks record...not good");
},1000)
}
}
componentDidMount() {
this.startCounter() // Call on initial render
}
componentWillReceiveProps(){
this.startCounter() //call on props change
}
render() {
return (
<div>
<p><span id="seconds">00</span>:<span id="tens">00</span></p>
</div>
)
}
}
Upvotes: 0