🦔

一緒にReactで三目並べを作ろ!①

2023/04/07に公開

まさかの、一緒に三目並べをしよ!ではなく、三目並べを作ろ!です。
以前、Hello World!をしようというテーマでReact初心者向けに記事を書きました。
https://zenn.dev/chizu_puzzle/articles/fd468a8ff3a35c

今回参考にしたサイトはこちらです。コードも引用しています。
https://react.dev/learn/tutorial-tic-tac-toe

準備

ではまず tic-tac-toe というプロジェクトフォルダを作成しましょうか。
ターミナルで下記コードを実行します。

npx create-react-app tic-tac-toe

こんな感じでtic-tac-toeプロジェクトを作成してくれます。

styles.cssをチュートリアルのサイトから取得して、srcフォルダ内に配置しましょう。
index.jsの内容を下記に合わせます。

index.js
import React, { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./styles.css";

import App from "./App";

const root = createRoot(document.getElementById("root"));
root.render(
  <StrictMode>
    <App />
  </StrictMode>
);

この状態でターミナルでnpm startをすると、

App.jsにはまだ命令を書いていないので、何も表示されません。

碁盤を表示させる

早速三目並べの1つ目の碁盤をJSX要素を追加して作ってみましょう。

App.js
export default function Square() {
  return <button className="square">X</button>;
}

ブラウザには、1つ目の碁盤とXが表示されました。

ここでキーワードの解説です。
export・・・ファイルの外部からこの関数を参照できる。
default・・・App.jsのメイン関数であることを知らせる。

試しに2つ目の碁盤も表示させてみます。

App.js
export default function Square() {
  return (
    <>
      <button className="square">X</button>
      <button className="square">X</button>
    </>
  );
}

HTMLではbuttonタグをくっつけて続けて書いてもエラーが出ないかもしれませんが、
Reactでは単一行で書いてあげる必要があります。

碁盤を下に追加するにはこうです。

App.js
export default function Square() {
  return (
    <>
      <div className="board-row">
	<button className="square">X</button>
        <button className="square">X</button>
      </div>
      <div className="board-row">
        <button className="square">X</button>
        <button className="square">X</button>
      </div>
    </>
  );
}

ここまでで、碁盤を追加していくことができました。
しかし、同じ部品を何個も並べるのってなんだか効率が悪い気がしますよね。
1つ2つなら良いものの、1000個並べるとかになるととてもやってられん!って感じです。

というわけで部品化をしていきます。

App.js
function Square() {
  return <button className="square">X</button>;
}
export default function Board() {
  return (
    <>
      <div className="board-row">
        <Square />
        <Square />
        <Square />
      </div>
      <div className="board-row">
        <Square />
        <Square />
        <Square />
      </div>
      <div className="board-row">
        <Square />
        <Square />
        <Square />
      </div>
    </>
  );
}

ブラウザではこのような表示になりました。

部品化はできましたが、碁盤にはそれぞれ「X」が入ってしまっています。
今度は碁盤上の情報を変更していきます。

App.js
function Square({ value }) {
  return <button className="square">{ value }</button>;
}

碁盤がまっさらになりましたね!

この状態では{ value }に何も情報が入っていませんので、情報を渡してあげましょう。

App.js
function Square({ value }) {
  return <button className="square">{ value }</button>;
}
export default function Board() {
  return (
    <>
      <div className="board-row">
        <Square value="1" />
        <Square value="2" />
        <Square value="3" />
      </div>
      <div className="board-row">
        <Square value="4" />
        <Square value="5" />
        <Square value="6" />
      </div>
      <div className="board-row">
        <Square value="7" />
        <Square value="8" />
        <Square value="9" />
      </div>
    </>
  );
}


Squareで受け取った値を碁盤上に表示してくれました。

クリックしたら塗りつぶす

マスの上をクリックしたらそこを塗りつぶすようにしたいと思います。
ひとまず、クリックしたらコンソールにログを出力するようにしてみましょう。
部品であるSquareの中にクリックしたときの処理を書いてあげるということになりますね。

App.js
function Square({ value }) {
  function handleClick() {
    console.log('clicked!');
  }

  return (
    <button
      className="square"
      onClick={handleClick}
    >
      {value}
    </button>
  );
}

マスの上をクリックすると、コンソールに「clicked!」と出力されているでしょうか。

では、クリックしたら碁盤に「X」が表示させるようにしましょう。
そして、クリックされたことを記憶するためにuseStateを使用します。

App.js
import { useState } from 'react';
function Square() {
  const [value, setValue] = useState(null);

  function handleClick() {
    setValue('X');
  }

  return (
    <button
      className="square"
      onClick={handleClick}
    >
      {value}
    </button>
  );
}

注意点ですが、先頭にuseStateをインポートします。
Squareの{ value }は削除し、useStateに初期値としてnullを渡します。

+import { useState } from 'react';
-function Square({ value }) {
+function Square() {
+  const [value, setValue] = useState(null);

Squareは{ value }を受け取らなくなったので、BoardのSquareのvalueも削除しましょう。

App.js
export default function Board() {
  return (
    <>
      <div className="board-row">
        <Square />
        <Square />
        <Square />
      </div>
      <div className="board-row">
        <Square />
        <Square />
        <Square />
      </div>
      <div className="board-row">
        <Square />
        <Square />
        <Square />
      </div>
    </>
  );
}

クリックしたところに「X」が表示されるようになりました!

まとめ

今回はここまで。
クリックするとマスの上に「X」が表示されるようになりました。
ここまでのApp.jsのコードの全体を載せます。

App.js
import { useState } from 'react';
function Square() {
  const [value, setValue] = useState(null);

  function handleClick() {
    setValue('X');
  }

  return (
    <button
      className="square"
      onClick={handleClick}
    >
      {value}
    </button>
  );
}
export default function Board() {
  return (
    <>
      <div className="board-row">
        <Square />
        <Square />
        <Square />
      </div>
      <div className="board-row">
        <Square />
        <Square />
        <Square />
      </div>
      <div className="board-row">
        <Square />
        <Square />
        <Square />
      </div>
    </>
  );
}

しかし、これだとまだ三目並べではないですね。
次回は対戦ができるようにしていきましょう。

Discussion