2017-05-06 52 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