質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

Q&A

解決済

1回答

3760閲覧

配列のStateにconcatしたい

dyekv

総合スコア128

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

0グッド

1クリップ

投稿2020/03/25 07:51

Reactのチュートリアル(三目並べ)を終わらせて追加課題の1つ目をやっています。

1.履歴内のそれぞれの着手の位置を (col, row) というフォーマットで表示する。

座標を表示させるために2次元配列にして座標の表示機能はなんとか実装できました。

しかし、チュートリアル終了時点ではできていたはずの過去の盤面に戻る機能が壊れてしまいました。

propのhistory(履歴)の中身を見てみると1手目〜最新手まですべて最新のsquares(盤面)になっていました。

handleClick内のsetStateがうまく出来ていないのだと思います。(74行目あたり)

sliceで複製できるのは1次元配列だけという記事も見ました。

それでもsliceしないと戻った後に別の手を実行した際の矛盾が解決できないと思います。

2次元配列を使わないようにすればできるかもしれませんが、2次元配列を使って出来るようにしたいです。

javascript

1import React from 'react'; 2import ReactDOM from 'react-dom'; 3import './index.css'; 4 5function Square(props) { 6 return ( 7 <button className="square" onClick={props.onClick}> 8 {props.value} 9 </button> 10 ); 11} 12 13class Board extends React.Component { 14 renderSquare(col, row) { 15 return ( 16 <Square 17 value={this.props.squares[col][row]} 18 onClick={() => this.props.onClick(col, row)} 19 /> 20 ) 21 } 22 render() { 23 return ( 24 <div> 25 <div className="board-row"> 26 {this.renderSquare(0, 0)} 27 {this.renderSquare(0, 1)} 28 {this.renderSquare(0, 2)} 29 </div> 30 <div className="board-row"> 31 {this.renderSquare(1, 0)} 32 {this.renderSquare(1, 1)} 33 {this.renderSquare(1, 2)} 34 </div> 35 <div className="board-row"> 36 {this.renderSquare(2, 0)} 37 {this.renderSquare(2, 1)} 38 {this.renderSquare(2, 2)} 39 </div> 40 </div> 41 ); 42 } 43} 44 45class Game extends React.Component { 46 constructor(props) { 47 super(props); 48 var squares = new Array(3); 49 for (let y = 0; y < 3; y++) { 50 squares[y] = new Array(3).fill(null); 51 } 52 this.state = { 53 history: [{ 54 squares: squares, 55 coordinate: { 56 col: null, 57 row: null, 58 } 59 }], 60 stepNumber: 0, 61 xIsNext: true, 62 } 63 } 64 65 handleClick(col, row) { 66 const history = this.state.history.slice(0, this.state.stepNumber + 1); 67 const current = history[history.length - 1]; 68 const squares = current.squares.slice(); 69 if (calculateWinner(squares) || squares[col][row]) { 70 return; 71 } 72 squares[col][row] = this.state.xIsNext ? 'X' : 'O'; 73 74 this.setState({ 75 history: history.concat({ 76 squares: squares, 77 coordinate: { 78 col: col, 79 row: row, 80 } 81 }), 82 stepNumber: history.length, 83 xIsNext: !this.state.xIsNext, 84 }); 85 } 86 87 jumpTo(step) { 88 this.setState({ 89 stepNumber: step, 90 xIsNext: (step % 2) === 0, 91 }) 92 } 93 94 render() { 95 const history = this.state.history; 96 const current = history[this.state.stepNumber]; 97 const winner = calculateWinner(current.squares); 98 99 const moves = history.map((step, move) => { 100 const desc = move ? 101 'Go to move #' + move + ' (' + step.coordinate.col + ',' + step.coordinate.row + ')' : 102 'Go to game start'; 103 return ( 104 <li key={move}> 105 <button onClick={() => this.jumpTo(move)}>{desc}</button> 106 </li> 107 ); 108 }); 109 110 let status; 111 if (winner) { 112 status = "Winner: " + winner; 113 } else { 114 status = "Next player: " + (this.state.xIsNext ? "X" : "O"); 115 } 116 117 return ( 118 <div className="game"> 119 stepNumber:{this.state.stepNumber} 120 <div className="game-board"> 121 <Board 122 squares={current.squares} 123 onClick={(col, row) => this.handleClick(col, row)} 124 /> 125 </div> 126 <div className="game-info"> 127 <div>{status}</div> 128 <ol>{moves}</ol> 129 </div> 130 </div> 131 ); 132 } 133} 134ReactDOM.render( 135 <Game />, 136 document.getElementById('root') 137); 138function calculateWinner(squares) { 139 for (let x = 0; x < 3; x++) { 140 if (squares[x][0] && squares[x][0] === squares[x][1] && squares[x][0] === squares[x][2]) { 141 return squares[x][0]; 142 } 143 } 144 for (let y = 0; y < 3; y++) { 145 if (squares[0][y] && squares[0][y] === squares[1][y] && squares[0][y] === squares[2][y]) { 146 return squares[0][y]; 147 } 148 } 149 if (squares[0][0] && squares[0][0] === squares[1][1] && squares[0][0] === squares[2][2]) { 150 return squares[0][0]; 151 } 152 if (squares[0][2] && squares[0][2] === squares[1][1] && squares[0][2] === squares[2][0]) { 153 return squares[0][2]; 154 } 155 return null; 156}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

ざっとソースを見た限りで、「ここに問題あり」と思われる箇所を回答します。

squares は 3×3の二次元配列なので、ご質問にある、以下のコード

修正前:

javascript

1const squares = current.squares.slice();

だと、完全な(深い)コピーを作ることになっていないです。上記を以下のように修正すれば、外側の配列の各要素である、内側の配列についてもコピーに置き換えられた squares が得られます。

修正後:

javascript

1const squares = current.squares.map(ary => ary.slice());

以下は、上記2つのコードの違いを確認するサンプルです。

上記の修正のみで、アプリ全体として意図通り動作するかは分かりませんが、上記の修正により何らかの進展はあると思います。

参考までに、プログラミング一般の用語でいえば、修正前のコードで作っているコピーは浅いコピー(Shallow Copy)、修正後のコードによるコピーは深いコピー(Deep Copy)と呼ばれます。"Shallow CopyとDeep Copyの違い" などで検索すれば多数の記事がヒットします。

補足

初期状態のsquares を得るための

javascript

1var squares = new Array(3); 2for (let y = 0; y < 3; y++) { 3 squares[y] = new Array(3).fill(null); 4}

は、以下のように1行で書くこともできます。

javascript

1var squares = [...Array(3)].map(_ => Array(3).fill(null));

投稿2020/03/25 20:07

jun68ykt

総合スコア9058

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dyekv

2020/03/26 02:05

ご回答ありがとうございます。 サンプルまで用意して頂き、とても丁寧な解説で分かりやすかったです。 ご回答していただいた内容で履歴に戻れない件について無事解決できました。 Shallow CopyとDeep Copyの違いについて知らなかったので勉強になりました。 アプリ全体としては「履歴から過去の盤面に戻って別の手を実行したときに、履歴が残ったままになる」という課題があります。これも同様にhistoryをsliceでコピーしているところを工夫すれば解決できるのかなと思いますので時間が空いたらやってみます。 ありがとうございました!
jun68ykt

2020/03/26 02:14

どういたしまして。 > ・・・ 無事解決できました。 とのことでよかったです????
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問