【React】公式のチュートリアルをやってみる～②プロジェクト用ファイル作成

[【React】公式のチュートリアルをやってみる～①環境構築](https://qiita.com/asahina820/items/9d7b1a8405c51d392afe) の続きです。
プロジェクトファイルを作成して、プログラムを読んでみました。

## ソースファイル配置
- src フォルダ内のファイルを削除する
- index.css, index.js を作成する
```index.css
body {
  font: 14px "Century Gothic", Futura, sans-serif;
  margin: 20px;
}

ol, ul {
  padding-left: 30px;
}

.board-row:after {
  clear: both;
  content: "";
  display: table;
}

.status {
  margin-bottom: 10px;
}

.square {
  background: #fff;
  border: 1px solid #999;
  float: left;
  font-size: 24px;
  font-weight: bold;
  line-height: 34px;
  height: 34px;
  margin-right: -1px;
  margin-top: -1px;
  padding: 0;
  text-align: center;
  width: 34px;
}

.square:focus {
  outline: none;
}

.kbd-navigation .square:focus {
  background: #ddd;
}

.game {
  display: flex;
  flex-direction: row;
}

.game-info {
  margin-left: 20px;
}
```

```index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';

class Square extends React.Component {
  render() {
    return (
      <button className="square">
        {/* TODO */}
      </button>
    );
  }
}

class Board extends React.Component {
  renderSquare(i) {
    return <Square />;
  }

  render() {
    const status = 'Next player: X';

    return (
      <div>
        <div className="status">{status}</div>
        <div className="board-row">
          {this.renderSquare(0)}
          {this.renderSquare(1)}
          {this.renderSquare(2)}
        </div>
        <div className="board-row">
          {this.renderSquare(3)}
          {this.renderSquare(4)}
          {this.renderSquare(5)}
        </div>
        <div className="board-row">
          {this.renderSquare(6)}
          {this.renderSquare(7)}
          {this.renderSquare(8)}
        </div>
      </div>
    );
  }
}

class Game extends React.Component {
  render() {
    return (
      <div className="game">
        <div className="game-board">
          <Board />
        </div>
        <div className="game-info">
          <div>{/* status */}</div>
          <ol>{/* TODO */}</ol>
        </div>
      </div>
    );
  }
}

// ========================================

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Game />);
```

- `npm start`を実行してサーバを起動すると、空の三目並べの盤面が表示される
![ReactApp2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667439/91f5fa1e-f454-b11c-6a02-51c85bf432bd.png)

・・・よく分からない！！！ので、現在のプログラムを解析してみる

## プログラム解析

### ページ表示用のテンプレート
```public/index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <!--
      manifest.json provides metadata used when your web app is installed on a
      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
    -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enabe JavaScript to run this app.</noscript>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
  </body>
</html>
```
- 先ほど表示されたページのテンプレートファイル

```html
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
```

- linkタグのhrefの`"%PUBLIC_URL%/`はビルド時にpublicフォルダへのパスに置き換わる

```html
<div id="root"></div>
```

- divタグの`id="root"`に対して、Reactのコードが埋め込まれる
    - React DOM によって管理されることになる

### JavaScript のエントリーポイント
```src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';

class Square extends React.Component {
  render() {
    return (
      <button className="square">
        {/* TODO */}
      </button>
    );
  }
}

class Board extends React.Component {
  renderSquare(i) {
    return <Square />;
  }

  render() {
    const status = 'Next player: X';

    return (
      <div>
        <div className="status">{status}</div>
        <div className="board-row">
          {this.renderSquare(0)}
          {this.renderSquare(1)}
          {this.renderSquare(2)}
        </div>
        <div className="board-row">
          {this.renderSquare(3)}
          {this.renderSquare(4)}
          {this.renderSquare(5)}
        </div>
        <div className="board-row">
          {this.renderSquare(6)}
          {this.renderSquare(7)}
          {this.renderSquare(8)}
        </div>
      </div>
    );
  }
}

class Game extends React.Component {
  render() {
    return (
      <div className="game">
        <div className="game-board">
          <Board />
        </div>
        <div className="game-info">
          <div>{/* status */}</div>
          <ol>{/* TODO */}</ol>
        </div>
      </div>
    );
  }
}

// ========================================

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Game />);
```

- JavaScriptのエントリーポイントとなっているファイル
- 3つのコンポーネントが定義されている
    - Square（正方形のマス目）
    - Board（盤面）
    - Game
- Square（マス目）コンポーネントは 1 つの `<button>` をレンダーし、Board（盤面）が 9 個のマス目をレンダーしていて、Game コンポーネントは盤面とプレースホルダーを描画している
    - Game → Board → Square


```jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
```
- npmでインストールしたモジュールをインポートする
- React
    - React ライブラリのエントリーポイント
- ReactDOM
    - アプリのトップレベルで使うための DOM 特有のメソッドを提供している

```jsx
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Game />);
```

- React要素をルートDOMノードにレンダーするには、ReactDOM.createRoot() にDOM要素を渡し、root.render() にReact 要素を渡す
    - ここでは、`Game`というReactコンポーネントが渡されている
        - ユーザ定義のコンポーネントの名前は大文字で始める

#### Gameコンポーネント
```jsx
class Game extends React.Component {
  render() {
    return (
      <div className="game">
        <div className="game-board">
          <Board />
        </div>
        <div className="game-info">
          <div>{/* status */}</div>
          <ol>{/* TODO */}</ol>
        </div>
      </div>
    );
  }
}
```
- Reactコンポーネントクラス`Game`を定義している
    - Reactでは、UI を「コンポーネント」と呼ばれる小さく独立した部品から組み立てることができる
- `render()` は、コンポーネントクラスで必ず定義しなければならない唯一のメソッド
    - JSX構文で作成されたReact要素を返す
        - JSXとは、JavaScriptにマークアップを書くことができる構文で、BabelでJavaScriptに変換される
- CSS のクラスをコンポーネントに適用するには、`className`プロパティにクラス名を指定する
- JSX内で、`Board`というReactコンポーネントが呼ばれている
    - コンポーネントは自身の出力の中で他のコンポーネントを参照できる

#### Boardコンポーネント
```jsx
class Board extends React.Component {
  renderSquare(i) {
    return <Square />;
  }

  render() {
    const status = 'Next player: X';

    return (
      <div>
        <div className="status">{status}</div>
        <div className="board-row">
          {this.renderSquare(0)}
          {this.renderSquare(1)}
          {this.renderSquare(2)}
        </div>
        <div className="board-row">
          {this.renderSquare(3)}
          {this.renderSquare(4)}
          {this.renderSquare(5)}
        </div>
        <div className="board-row">
          {this.renderSquare(6)}
          {this.renderSquare(7)}
          {this.renderSquare(8)}
        </div>
      </div>
    );
  }
}
```

- マスの1行ごとに3回`renderSquare()`を実行している
    - JSXでは、`{}`で囲むことで式を埋め込むことができる
- ステータスとして、`Next player: X`を表示している
    - この文字列は、定義した`status`変数を使用している
- `renderSquare`関数では、`Square`というReactコンポーネントが返される

#### Squareコンポーネント
```jsx
class Square extends React.Component {
  render() {
    return (
      <button className="square">
        {/* TODO */}
      </button>
    );
  }
}
```
- 1つのボタンを返している

## 参考文献
- https://ja.reactjs.org/docs/rendering-elements.html
- https://create-react-app.dev/docs/folder-structure
- https://ja.reactjs.org/docs/react-api.html
- https://ja.reactjs.org/docs/react-component.html
- https://ja.reactjs.org/docs/introducing-jsx.html
- https://ja.reactjs.org/docs/faq-styling.html