📘

精神と時の部屋日記1 | Reactチュートリアル

2023/03/07に公開

Reactを触れてみようということで、チュートリアルを開始したので日記形式でやっていきたいと思います。
チュートリアル


  1. 初めにローカルでの開発環境を作る
    多分オンラインでライブコードエディターがあってそちらでやれると書いてるんですが、vscodeでやりたかったのでローカルで環境を作ります。以下はドキュメントから引用しています。
  • ローカルの開発環境を使用して、このチュートリアルに従うこともできます。これを行うには、次のことが必要です。
  • Node.jsをインストールする
  • 前に開いた CodeSandbox タブで、左上隅のボタンを押してメニューを開き、そのメニューで[ファイル] > [ZIP にエクスポート]を選択して、ファイルのアーカイブをローカルにダウンロードします。
  • アーカイブを解凍し、ターミナルを開き、cd解凍したディレクトリに移動します
  • 依存関係をインストールしますnpm install
  • 実行してnpm startローカル サーバーを起動し、プロンプトに従ってブラウザーで実行されているコードを表示します。

手順通りにやります。node.jsのインストールですが
こんな画面が出てくるのでLTSの方を選択してインストールします。LTSとCurrentの違いは一番おすすめのバージョンor最新版という感じです。

インストールしたら、チュートリアルのフォークをクリックして、codesandboxにあるファイルをZIP形式でダウンロードします。手順通りでおけです。解答したら、vscodeからファイルを開きます。コマンドを立ち上げて

npm install
npm start

を実行し、ローカルサーバーが立ち上がります。私はここで下記のエラー文が出てきました。

  • ./src/App.js のコンパイルに失敗しました 2 行目: JSX を使用する場合は「React」がスコープ内にある必要があります react/react-in-jsx-scope キーワードを検索して、各エラーの詳細を確認してください。

src/App.jsにReactがインポートされていないから起きるっぽいので、一行目に下記を追加します。

import React from 'react';

これでエラーが解消し、
この画面が出てきました。


  1. 構築
    三目並べを作っていく。
export default function Square() {
  return (
    <>
      <button className="square">X</button>
      <button className="square">X</button>
    </>
  );
}

次はここでエラーが出ます。

./src/App.js
Syntax error: Unexpected token (4:5)

2 | export default function Square() {
3 | return (

4 | <>
| ^
5 | <button className="square">X</button>
6 | <button className="square">X</button>
7 | </>

このフラグメントで結構つまづいたのでおさらい。

フラグメント

  • フラグメントfragmentはreactのコンポーネントが複数の要素を返すとき、DOMに余分なノードを追加することなく子要素をまとめることができるもの。
  • DOMとはhtmlを使うためのAPI
  • ノードとは仮想DOM上の要素
    記述の例
render() {
return (
  <React.Fragment>
    <ChildA />
    <ChildB />
    <ChildC />
  </React.Fragment>
);
}

このReact.Fragmentを短縮して、
<>
</>
と書かれることがある。
versionが更新されていないのかわからないが短縮の方でかくとエラーでるので、短縮せずに書いていく。

import React from 'react';
export default function Square() {
  return (
    <React.Fragment>
      <div className="board-row">
        <button className="square">1</button>
        <button className="square">2</button>
        <button className="square">3</button>
      </div>
      <div className="board-row">
        <button className="square">4</button>
        <button className="square">5</button>
        <button className="square">6</button>
      </div>
      <div className="board-row">
        <button className="square">7</button>
        <button className="square">8</button>
        <button className="square">9</button>
      </div>
    </React.Fragment>
  );
}

これで1~9までの正方形が3*3の正方形の形で出力された。

  • propsとは 親のコンポーネントから子コンポーネントに値valueを渡すための仕組み。

  • macOSであればOption + ⌘ + Jでブラウザーのコンソールからクリックしたログが見れる。

とりあえずここまでエラーは出ません。react.fragmentの短縮と一行目のimportのみがチュートリアルと違う部分でした。

import React from 'react';
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 (
    <React.Fragment>
      <div className="board-row">
        <Square />
        <Square />
        <Square />
      </div>
      <div className="board-row">
        <Square />
        <Square />
        <Square />
      </div>
      <div className="board-row">
        <Square />
        <Square />
        <Square />
      </div>
    </React.Fragment>
  );
}

途中経過

  • コンポーネントをいじる際には外部から直接いじることはできないから関数を渡して操作する。
  • コンポーネントのpropsはfunction name ({props}) これにより親コンポーネントが子コンポーネントを操作することができる。
  • コンポーネントの親子関係
    親:コンポーネントを利用する側
    子:コンポーネントを利用される側
    コンポーネントは<></>で囲むことで呼び出せる。

(例)
親コンポーネント
<Title title="xxx"/>
Title=コンポーネントの呼び出し
title="xxx"/=渡したい値

子コンポーネント
<Text>{this.props.title}</Text>
{this.props.title}=propsからtitleの値を取り出す。

よって表示されるのはxxx

  • propsで渡せる値は文字列やスタイル
  • 利点はコンポーネントの再利用。
  • 子から親へ値を渡すことはできない。動的な変更もできない。
  • ここではSquare関数が子コンポーネントでBoard関数が親。
  • ゲームの状態を保存してるのが子コンポーネント
  • var, let, constはjsの変数や定数の宣言方法。constは値valueへの読み取り専用の参照を作るもの。
import React from 'react';
import { useState } from 'react';
function Square({value, onSquareClick}) {
  return <button className="square" onClick={onSquareClick}>{value}</button>;
}
export default function Board() {
  const [squares, setSquares] = useState(Array(9).fill(null));
  
  function handleClick() {
    const nextSquares = squares.slice();
    nextSquares[0] = "X";
    setSquares(nextSquares);
  }
  return (
    <React.Fragment>
      <div className="board-row">
        <Square value={squares[0]} onSquareClick={handleClick}/>
        <Square value={squares[1]} />
        <Square value={squares[2]} />
      </div>
      <div className="board-row">
        <Square value={squares[3]} />
        <Square value={squares[4]} />
        <Square value={squares[5]} />
      </div>
      <div className="board-row">
        <Square value={squares[6]} />
        <Square value={squares[7]} />
        <Square value={squares[8]} />
      </div>
    </React.Fragment>
  );
}

コードおさらい

import React from 'react';
import { useState } from 'react';

function Square({value, onSquareClick}) {
  return <button className="square" onClick={onSquareClick}>{value}</button>;
}

export default function Board() {
  const [xIsNext, setXIsNext] = useState(true);
  const [squares, setSquares] = useState(Array(9).fill(null));

  function handleClick(i) {
    if (squares[i] || calculateWinner(squares)) {
      return;
    }
    const nextSquares = squares.slice();
    if (xIsNext) {
      nextSquares[i] = "X";
    } else {
      nextSquares[i] = "O";
    }
    setSquares(nextSquares);
    setXIsNext(!xIsNext);
  }

  const winner = calculateWinner(squares);
  let status;
  if (winner) {
    status = "Winner: " + winner;
  } else {
    status = "Next player: " + (xIsNext ? "X" : "O");
  }
  return (
    <React.Fragment>
      <div className="status">{status}</div>
      <div className="board-row">
        <Square value={squares[0]} onSquareClick={() => handleClick(0)} />
        <Square value={squares[1]} onSquareClick={() => handleClick(1)} />
        <Square value={squares[2]} onSquareClick={() => handleClick(2)} />
      </div>
      <div className="board-row">
        <Square value={squares[3]} onSquareClick={() => handleClick(3)} />
        <Square value={squares[4]} onSquareClick={() => handleClick(4)} />
        <Square value={squares[5]} onSquareClick={() => handleClick(5)} />
      </div>
      <div className="board-row">
        <Square value={squares[6]} onSquareClick={() => handleClick(6)} />
        <Square value={squares[7]} onSquareClick={() => handleClick(7)} />
        <Square value={squares[8]} onSquareClick={() => handleClick(8)} />
      </div>
    </React.Fragment>
  );
}

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

タイムトラベルの追加

  • history配列を作成してsquaresの履歴を格納していく。boardでは三目並べの機能を入れているため、新たにGame関数を作る。最上位のコンポーネントをGameと認識させるためにexport default宣言をboardから削除する。
  • const [xIsNext, setXIsNext] = useState(true);
    「xIsNext」という状態変数を作成し、「setXIsNext」という関数を使用してその値を更新するコード
  • const [history, setHistory] = useState([Array(9).fill(null)]);
    「history」という状態変数を作成し、「setHistory」という関数を使用してその値を更新するコード
  • const currentSquares = history[history.length - 1];
    「history.length - 1」を使用して「history」配列の最後の要素にアクセスすることにより、ゲーム ボードの現在の状態を取得するコード

Discussion