複数の子コンポーネントからデータを収集したい、あるいは 2 つの子コンポーネント同士で通信したい、と思ったら、代わりに親コンポーネントに共有の state を宣言するようにしてください。親コンポーネントはその state を子コンポーネントに prop 経由で渡すことができます。これにより、子同士および親子間で、コンポーネントが同期されるようになります。
- JavaScriptの配列は参照型のデータです。これは、変数に配列を代入すると、その変数は実際の配列データそのものを指しているということを意味します。そのため、ある変数に代入された配列を別の変数に直接代入すると、両方の変数が同じ配列の参照を持つことになります。
例えば:
let original = [1, 2, 3];
let copy = original;
copy[0] = 99;
console.log(original[0]); // 99
上記のコードでは、copy
とoriginal
は同じ配列を指しているため、copy
を通じて配列を変更するとoriginal
も影響を受けます。
このような挙動を避けるために、配列のコピーを作成する方法が必要です。ここでslice()
メソッドが役立ちます。slice()
は配列の部分的なシャローコピー(浅いコピー)を作成します。引数なしで呼び出すと、全体のコピーが作成されます。
let original = [1, 2, 3];
let copy = original.slice();
copy[0] = 99;
console.log(original[0]); // 1
この場合、original
の内容は変更されません。
ただし、注意点としてslice()
はシャローコピーを行うので、配列の中にさらに配列やオブジェクトがある場合、その内部の配列やオブジェクトは参照がコピーされるだけで、新しいものが作成されません。このような深いネストを持つデータ構造の完全なコピーを作成するには、他の方法(例: 深いコピー)が必要です。
-
質問
- onSquareClick={handleClick} のようにしていたときは、props として handleClick 関数を渡していました。呼び出してはいませんでした! しかし、今はその関数をその場で呼び出してしまっているのです。handleClick(0) の括弧の部分に注目してください。だからすぐに実行されてしまうのです。ユーザがクリックするまで、handleClick を呼び出したくないわけです。
- しかし、今はその関数をその場で呼び出してしまっているのです。handleClick(0) の括弧の部分に注目してください。だからすぐに実行されてしまうのですしまうのです。ここがよくわからない
-
了解しました。JavaScript(および多くのプログラム言語)で関数を呼び出す際、関数の名前の後ろに括弧
()
を付けることで、その関数が即座に実行されます。
例を挙げると:
function sayHello() {
console.log("Hello!");
}
sayHello(); // この行で "Hello!" と表示されます。
ここで、sayHello
関数は sayHello()
のように括弧付きで呼び出されているため、その関数は実行されます。
Reactのコンポーネントのプロパティ(props)として関数を渡す場合、その関数を即座に実行するのではなく、あとでコールバックとして使用できるようにすることが一般的です。したがって、関数を引数なしで渡す場合は、括弧を使用せずにその名前だけを指定します。
例えば:
<Square onClick={sayHello} /> // これは正しい。SquareがクリックされたときにsayHelloが実行されます。
ところが、handleClick(0)
のように関数を括弧付きで呼び出すと、その関数は即座に実行され、その結果(return値)がコンポーネントのプロパティとして渡されます。
この例で言えば:
<Square onClick={handleClick(0)} /> // これはhandleClickが即座に実行されるので誤りです。
handleClick(0)
が即座に実行されるため、stateが更新され、それによって再レンダリングがトリガーされ、さらにhandleClick(0)
が再度実行される、という無限ループが発生します。
解決策の1つとして、関数を返すアロー関数を使用する方法があります:
<Square onClick={() => handleClick(0)} />
この書き方では、onClick
に渡されるのはhandleClick(0)
を実行するアロー関数です。このため、Square
がクリックされたときにのみ、handleClick(0)
が実際に実行されます。
-
左上のマス目をクリックすると、button が props として受け取った onClick 関数が実行されます。Square コンポーネントはその関数を Board から onSquareClick プロパティとして受け取っています。Board コンポーネントはその関数を JSX の中で直接定義しています。その関数は引数 0 で handleClick を呼び出します。
-
handleClick は引数 0 を使って、squares 配列の最初の要素を null から X に更新します。
-
Board コンポーネントの state である squares が更新されたので、Board とそのすべての子が再レンダーされます。これにより、インデックス 0 である Square コンポーネントの value プロパティが null から X に変更されます。