2017-02-27 73 views
0

正如我在反應主頁here中遵循教程一樣。我想修改x-o遊戲代碼以在遊戲中前進和後退。我包含下面的代碼。即使狀態發生變化,反應也不會使組件發生變化

歷史被記錄在歷史列表中,pos是列表上當前選擇的索引。你可以看到我有三個按鈕:復位,向上,向下。當您按Reset時,我重置狀態。當你按下時,我增加pos。當你按下時,我減少pos。我也顯示pos來檢查。

問題是pos正在更新,但組件沒有重新渲染。爲便於使用我包括heroku here

我在這裏失蹤了什麼?

import React, { Component } from 'react'; 
import './App.css'; 


function Square(props){ 
    return (
     <div className="square" onClick={() => props.onClick()}> 
      {props.value} 
     </div> 
    ); 
} 

function calculateWinner(squares){ 
    const winners = [ 
     [0, 1, 2], 
     [3, 4, 5], 
     [6, 7, 8], 
     [0, 4, 8], 
     [2, 4, 6], 
     [0, 3, 6], 
     [1, 4, 7], 
     [2, 5, 8] 
    ]; 
    for(let i = 0; i < winners.length; i++){ 
     const [a, b, c] = winners[i]; 
     if(squares[a] && squares[b] === squares[a] && squares[c] === squares[a]){ 
      return squares[a]; 
     } 
    } 
    return null; 
} 

class Board extends Component{ 
    constructor() { 
     super(); 
     this.state = { 
      history: [Array(9).fill(null)], 
      pos : 0, 
      xIsNext: true 
     } 
    } 

    handleClick(i){ 
     const current = this.state.history[this.state.pos]; 
     if (current[i] || calculateWinner(current)) { 

     } else { 
      const history = this.state.history.slice(); 
      const squares = history[this.state.pos].slice(); 
      squares[i] = this.state.xIsNext ? "X" : "O"; 
      history[this.state.pos + 1] = squares; 
      this.setState({ 
       history: history, 
       pos: this.state.pos + 1, 
       xIsNext: !this.state.xIsNext 
      }); 
     } 
    } 

    clickReset(){ 
     this.setState(
      { 
       history: [Array(9).fill(null)], 
       pos : 0, 
       xIsNext: true 
      } 
     ); 
    } 
    clickUp(){ 
     let pos = this.state.pos; 
     if(pos < this.state.history.length) { 
      pos += 1; 
      this.setState(
       { 
        pos: pos, 
        xIsNext: !this.state.xIsNext 
       } 
      ); 
     } 
    } 
    clickDown(){ 
     let pos = this.state.pos; 
     if(pos > 0) { 
      pos -= 1; 
      this.setState(
       { 
        pos: pos, 
        xIsNext: !this.state.xIsNext 
       } 
      ); 
     } 
    } 
    renderSquare(i){ 
     let current = this.state.history[this.state.pos]; 
     return (
      <Square value={current[i]} onClick={() => this.handleClick(i)}/> 
     ) 
    } 

    render() { 
     let status ; 
     const pos = this.state.pos; 
     const current = this.state.history[pos]; 
     const winnerCal = calculateWinner(current); 
     if (winnerCal){ 
      status = "Winner : " + winnerCal; 
     } else { 
      status = "Next Play : " + (this.state.xIsNext ? "X" : "O"); 
     } 
     return (
      <div className="col"> 

       <div className="row justify-content-center"> 
        <div className="board-row"> 
         {this.renderSquare(0)} 
         {this.renderSquare(1)} 
         {this.renderSquare(2)} 
        </div> 
        <div className="w-100"></div> 
        <div className="board-row"> 
         {this.renderSquare(3)} 
         {this.renderSquare(4)} 
         {this.renderSquare(5)} 
        </div> 
        <div className="w-100"></div> 
        <div className="board-row"> 
         {this.renderSquare(6)} 
         {this.renderSquare(7)} 
         {this.renderSquare(8)} 
        </div> 
       </div> 
       <div className="row justify-content-center"> 
        {status} pos: {pos} 
       </div> 
       <div className="row-fluid"> 
        <button type="button" className="col-md-4 btn btn-warning" onClick={() => this.clickReset()}>Reset</button> 
        <button type="button" className="col-md-4 btn btn-danger" onClick={() => this.clickUp()}>up</button> 
        <button type="button" className="col-md-4 btn btn-info" onClick={() => this.clickDown()}>down</button> 
       </div> 
      </div> 
     ) 
    } 
} 
class App extends Component { 

    renderHeader(){ 
     return (
      <div className="row justify-content-center"> 
       <div className="col"></div> 
       <div className="col"> 
        <h3 className="row justify-content-center"> Lets build a xo game </h3> 
       </div> 
       <div className="col"></div> 
      </div> 
     ) 
    } 

    renderBoard(){ 
     return (
      <div className="row"> 
       <div className="col"></div> 
       <Board className="col"/> 
       <div className="col"></div> 
      </div> 
     ) 
    } 

    render() { 
     return (
      <div className="container"> 
       {this.renderHeader()} 
       {this.renderBoard()} 
      </div> 
     ); 
    } 
} 

export default App; 

編輯:

在handleClick

()改變

const squares = history[this.state.pos] 
.. 
history[history.length] = squares; 

const squares = history[this.state.pos].slice(); 
.. 
history[this.state.pos + 1] = squares; 
+0

我在渲染函數中放置了一個斷點,每次單擊按鈕時都會調用它。 – paqash

+0

確定將問題更改爲更準確的問題 – StarLord

回答

1

你的做法是最有可能運作良好。問題是你的歷史被改寫

const squares = history[this.state.pos]; 
squares[i] = this.state.xIsNext ? "X" : "O"; 
history[history.length] = squares; 

導致歷史[history.length]引用同一個對象作爲歷史[history.length - 1]等降至歷史[0]

嘗試使用

const squares = history[this.state.pos].slice(); 
+0

解決了問題,但爲什麼應該處理ClickClick()鏈接點擊個別方塊會影響應呈現的內容。當我點擊「向上」或「向下」時,要更清楚一點,如果你可以說slice()在那裏的重要性,那麼handleClick()的依賴關係是一個onClick方法() – StarLord

+0

。這將非常有用 – StarLord

+0

您在handleClick()處更新了組件狀態,並且狀態更改觸發了新的呈現。當你調用setState時,你改變整個棋盤的狀態(根據this.state.history渲染)。 這個問題是由於修改了最初得到的同一個方格數組而導致的,它使歷史集合中的所有數組都引用了同一個實例。爲了讓每個新的歷史事件成爲前一步的副本,我使用了slice來獲得淺拷貝 – Igor

相關問題