如何通过从父组件作为prop传递的状态在子级中切换setInterval

时间:2018-02-01 06:43:53

标签: javascript reactjs

在jsbin完成工作示例: http://jsbin.com/tepedepozi/1/edit?console,output

_____________________________________________________________--

我有一个名为App的组件,完整返回的JSX就在这里:

 <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>

请注意,有两个按钮:录制按钮和播放按钮

然后用户点击播放按钮:

<Button buttonText = {this.state.playButtonText} onClick={this.toggleSongPlaying} />

调用将this.state.isPlaying更改为true的方法:

如果用户点击了记录按钮,它根本不会改变this.state.isPlaying - 永远!这是我编程的方式,这是我想要的行为!

TimeDisplay组件中,我将this.state.isPlaying的状态作为道具传递。

<TimeDisplay isPlaying = {this.state.isPlaying}/>

问题在于我想让TimeDisplay以基于此状态的方式运行,而TimeDisplay组件应该只根据用户点击来响应状态更改:

<Button buttonText = {this.state.playButtonText} onClick={this.toggleSongPlaying} />

但是,当用户单击播放按钮然后记录按钮时,记录按钮会强制在TimeDisplay组件上重新呈现。我通过在下面的startCount()方法代码中放置一个setInterval来证明这个问题。

TimeDisplay组件:

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>
        )
    }

}

2 个答案:

答案 0 :(得分:1)

您需要在componentDidMount方法中编写this.startCounter()函数,而不是在渲染中编写,因为setInterval将为您完成工作,您只需要调用setInterval一次而不是每次渲染。既然是从道具收到了播种,你还需要更新你的孩子柜台。您可以在componentDidUpdate中执行此操作,如

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>)
    }
}

<强> Working JSBIN

  

P.S。我使用componentDidUpdate而不是。{   componentWillReceiveProps因为React建议他们愿意   从componentWillReceiveProps开始重命名v16.3.0并删除   从第17节开始。查看this对话

答案 1 :(得分:0)

您需要在触发新内容之前取消间隔。另外@shubham建议像他建议的那样调用startCounter方法。

  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>
        )
    }

}