2017-05-06 36 views
1

我是新来的使用React和作为练习试图修改时钟示例 从React's splash page。我们的目标是在一个时间间隔内计算秒数,并以更快的间隔更改圆圈的颜色。反应和多个定时事件

我曾尝试用setTimeout功能的更改圈 要做到这一点,并呼吁他们从tick函数中,但setTimeout通话不被 认可。

这是React的问题,setIntervalsetTimeout冲突? 如果是这样,实现偏移定时事件的最佳方式是什么?

谢谢你的时间。

<!doctype html> 
<html> 
<head> 
<title>Timer</title> 

<script src="https://unpkg.com/[email protected]/dist/react.js"></script> 
<script src="https://unpkg.com/[email protected]/dist/react-dom.js"></script> 
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script> 
<link rel="stylesheet" href="styles.css" > 
</head> 

<body> 
<div id="root"></div> 
</body> 
<script type="text/babel"> 

var changed = false; 

class GameDisplay extends React.Component{ 
    constructor(props){ 
    super(props); 
    this.colorChange = this.colorChange.bind(this); 
    this.state = { 
     secondsElapsed: 0, 
    }; 
    } 

    tick(){ 
    setTimeout(() => this.colorChange(), 250); 
    setTimeout(() => this.colorChange(), 250); 
    this.colorChange(); 
    this.setState((prevState) => ({ 
     secondsElapsed: prevState.secondsElapsed + 1, 
    })); 
    } 

    colorChange(){ 
    var tgt = document.getElementById("heart"); 
    if (changed){ 
     tgt.style.fill = "red"; 
    } else { 
     tgt.style.fill = "green"; 
    } 
    changed = !changed; 
    } 

    componentDidMount(){ 
    this.interval = setInterval(() => this.tick(), 1000); 
    } 

    componentWillUnmount(){ 
    clearInterval(this.interval); 
    } 

    render(){ 
    return(
    <div> 
     <h1>Seconds Elapsed: {this.state.secondsElapsed}</h1> 
     <svg id="main_disp" width="300" height="300"> 
     <circle cx="150" cy="150" r="50" fill="black" /> 
     <circle id="heart" cx="150" cy="150" r="30" fill="red" /> 
     </svg> 
    </div> 
); 
    } 
} 

class App extends React.Component { 
    render(){ 
    return(
     <div> 
     <GameDisplay /> 
     </div> 
    ) 
    }; 
} 



ReactDOM.render(
    <App />, 
    document.getElementById("root") 
); 
</script> 
</html> 

回答

1

TL; DR你看不到改变的原因是,你必须在“同一时间”,一个覆盖其他两个colorChange执行。

setTimeout(() => this.colorChange(), 250); 
setTimeout(() => this.colorChange(), 250); 

的setTimeout是异步,并立即返回,所以你的时间“相同”的金额后调度两个colorChange执行。尝试将第二个呼叫超时后500ms,它的工作原理。

我在“同一时间”使用了引号,因为setTimeout不是那么准确(按设计),JS是单线程的,所以这两个执行发生在接近下一个250ms的地方,并按顺序进行。但是无论如何,它们发生得太快以至于眼睛不能遵循,而且大多数现代浏览器都会将批量渲染作为优化步骤进行,因此这种更改可能根本不会发生。

===

但是,因为你提到你不熟悉的反应,这是锻炼动手我想说的是,你可以做一些改变来写这更好的(在反应方面) 。看下面的例子:

<!doctype html> 
<html> 
    <head> 
    <title>Timer</title> 
    <script src="https://unpkg.com/[email protected]/dist/react.js"></script> 
    <script src="https://unpkg.com/[email protected]/dist/react-dom.js"></script> 
    <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.38/browser.min.js"></script> 
    <link rel="stylesheet" href="styles.css" > 
    </head> 
    <body> 
    <div id="root"></div> 
    <script type="text/babel"> 
     const merge = (x, y) => Object.assign({}, x, y) 
     class GameDisplay extends React.Component { 
     constructor(props) { 
      super(props) 
      this.state = { 
      fill: props.primaryFill, 
      secondsElapsed: 0 
      } 
      this.tick = this.tick.bind(this) 
      this.colorChange = this.colorChange.bind(this) 
     } 

     tick() { 
      this.colorChange(); 
      this.setState((prevState) => merge(prevState, { 
      secondsElapsed: prevState.secondsElapsed + 1, 
      })); 
      setTimeout(this.colorChange, 250) 
      setTimeout(this.colorChange, 500) 
      setTimeout(this.colorChange, 750) 
     } 

     colorChange() { 
      this.setState((prevState, props) => { 
      const { primaryFill, secondaryFill } = props 
      const oldFill = this.state.fill 
      const fill = oldFill === primaryFill ? secondaryFill : primaryFill 
      return merge(prevState, { fill }) 
      }) 
     } 

     componentDidMount() { 
      this.tick() 
      this.interval = setInterval(this.tick, 1000); 
     } 

     componentWillUnmount(){ 
      clearInterval(this.interval); 
     } 

     render() { 
      return (
      <div> 
       <h1>Seconds Elapsed: {this.state.secondsElapsed}</h1> 
       <svg id="main_disp" width="300" height="300"> 
       <circle cx="150" cy="150" r="50" fill="black" /> 
       <circle id="heart" cx="150" cy="150" r="30" fill={this.state.fill} /> 
       </svg> 
      </div> 
     ); 
     } 
     } 

     function App() { 
     return (
      <div> 
      <GameDisplay primaryFill="red" secondaryFill="green" /> 
      </div> 
     ) 
     } 

     ReactDOM.render(<App />, document.getElementById("root")); 
    </script> 
    </body> 
</html> 

突出一些最佳实践:

  1. 没有发生变异的DOM,反应恩惠数据的单向流动,在action > state > view,看到https://medium.com/@khbrt/react-unidirectional-data-flow-explained-71bc35858226(也有例外对于密集的动画,但作为一般规则,尽量避免吧)
  2. 如果你有变异它,试图让使用裁判API,而不是document.getElementById元素,请参阅https://facebook.github.io/react/docs/refs-and-the-dom.html
  3. 使用功能部件是否有上课的人没有理由, 看到https://facebook.github.io/react/docs/components-and-props.html
  4. 代码可能会更清洁,如果你使用一些ES6 +功能, 看到https://babeljs.io/blog/2015/06/07/react-on-es6-plus
+0

Mehiel非常感谢你的时间和引用,这是一个很大的帮助。谢谢。 – Cameron