React & TypeScript初学者のすゝめ 第三章

## 知っておくと便利なReact知識
前章までの知識でもReactを用いた開発は可能であるが、さらに見やすいコード、自由度の高いサービスを開発する上で必要不可欠な知識を以下にまとめていく。
## ページ遷移
ReactはStateを使ったりリアルタイム処理ができたり性質上ページ遷移の重要度が非常に高いわけではないが、PHPなどWebサービスでは基本的にページ遷移は絶対必要だし、Reactにおいてもそれができるようになることで可能性が一気に広がる

#### React Routerの導入
ページ遷移を使う上でRoutingを担うパッケージを入れる必要がある。
```
npm add react-router-dom
```
これを作成したプロジェクト下で実行することで準備が整う。
```App.tsx
// react-router-domのインポートを追加
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import { Link } from "react-router-dom";

import { Routing } from "./components/router/Routing";

class App extends React.Component {
  render(){
    return (
       <Router>
        <div className="App">
         <Link to="/">Home</Link>
         <br />
         <Link to="/page1">Page1</Link>
         <br />
         {/* 以下のやり方でページ遷移先にstateを渡すこともできる */}
         <Link to="/page2">Page2</Link>

         {/* コードの見易さと機能性向上のためRoutingを分ける */}
         <Routing />
        </div>
       </Router>
    )
  }
}

export default App;
```
ページ遷移を行うためには遷移適応範囲をRouterタグで囲い、aタグの代わりとしてLinkタグを使って画面遷移を行う。
次に実際にレンダリングされるフロントだけではどのファイルがどの画面を表示するのか決まらないため、Routing設定を行うが、この部分は<Routing />に記載する。

```Routing.tsx
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";

import { Home } from "../Home";
import { Page1 } from "../Page1";
import { Page1DetailA } from "../Page1DetailA";
import { Page1DetailB } from "../Page1DetailB";
import { Page2 } from "../Page2";
import { Page404 } from "../Page404";

export const Routing = () => {
  return (
    <Routes>
        <Route path="/" element={<Home />} />

        {/* Route設定に関して親ページも子ページも同階に設置する */}
        <Route path="/page1" element={<Page1 />} />
        <Route path="/page1/detailA" element={<Page1DetailA />} />
        <Route path="/page1/detailB" element={<Page1DetailB />} />

        <Route path="/page2" element={<Page2 />} />
        {/* 指定URLが存在しない場合ここに飛ぶ */}
        <Route path="/*" element={<Page404 />} />
    </Routes>
  )
}
```
どのファイルがどのページに紐付いてるかのRouting設定を行い、初めてページ遷移が可能になる。
次に別ページへのパラメータやクエリの渡し方について記述する。

#### 遷移先ページへパラメータを送る
初めに遷移先ページへのパラメータの渡し方について
```diff_javascript
export const Routing = () => {
  return (
    <Routes>
+       <Route path="/page1/:id" element={<Page1DetailA />} />
-       <Route path="/page1/detailA" element={<Page1DetailA />} />
        <Route path="/page1/detailB" element={<Page1DetailB />} />
    </Routes>
  )
}
```
まずURLの部分にパラメータを埋め込むため、pathの変更を行う。
ちなみにパラメータ「/:id」は受取先と変数名が異なるとうまくいかない。
```Page1DetailA.tsx
import { useParams } from "react-router-dom";
export const Page1DetailA = () => {
  //URLパラメーターのIDを受け取るが、{}を忘れるとエラーになる
  const {id} = useParams();
  return (
    <div>
      <h1>DetailA</h1>
      <p>パラメーターは{id}です</p>
    </div>
  );
};
```
そして、実際にPage1DetailAに遷移させる機能を担っているのは親ページPage1の記述は以下の通り。
```Page1.tsx
import { Link } from "react-router-dom";

export const Page1 = () => {
  return (
    <div>
      <h1>Page1</h1>
      <Link to="/page1/10">DetailA</Link>
      <br />
      {/* クエリを使う際はRouting設定を変更する必要はない */}
      <Link to="/page1/detailB?name=mere">DetailB</Link>
    </div>
  );
};
```
#### 遷移先ページへクエリを送る
```Page1DetailB.tsx
import { useLocation } from "react-router-dom";

export const Page1DetailB = () => {
  // クエリを受け取る
  const {search} = useLocation();
  const query = new URLSearchParams(search);
  return (
    <div>
      <h1>DetailB</h1>
      <p>クエリは{query.get("name")}です</p>
    </div>
  );
};
```
クエリの場合はRouting設定を変更する必要はない。
クエリの送り方は上述のPage1.tsxの通りである。
#### 遷移先ページへStateとして値を送る
最後に別ページにStateとして値を送る方法について記述する。
```diff_javascript
render(){
    const sta = ["メロン", "デュポン", "カーネギー"];
    return (
       <Router>
        <div className="App">
+        <Link to="/page2" state={{ sta }}>Page2</Link>
-        <Link to="/page2">Page2</Link>
        </div>
       </Router>
    )
  }
```
Linkタグの引数としてStateを渡す。Stateは文字列でもリストでも可能なところが便利である。
```Page2.tsx
import { useLocation } from "react-router-dom";

export const Page2 = () => {
  // const state = {sta: ["メロン", "デュポン", "カーネギー"]}という形で出力される
  const { state } = useLocation();

  return (
    <div>
      <h1>Page2</h1>
      <ul>
      {state.sta.map((_list: string, _key: number) => {
        return(
            <li key={_key}>{_list}</li>
        )})}
      </ul>
    </div>
  );
};
```

## APIを操作してみる
今のWebサービスで外部APIを利用するケースは大変多く、ページ遷移と並んで必要不可欠な機能であると考えられる。
実際皆が思うほど難しくもないので軽く触れていく。
```Home.tsx
export const Home = () => {
  const url = "https://api.github.com/users/ogawanobuya"
  const apiPractice = () => {
    fetch(url) 
    .then(response => {
      // response.json()も非同期処理でレスポンスからJSON形式データを取得する関数
      response.json()
      .then(json => {
        console.log("ちゃんと動いている");
        console.log(json)
      })
      .catch(error => {
        console.log("読み取り失敗2");
      });
    })
    .catch(error => {
      console.log("読み取り失敗1");
    });
  }

  return (
    <div>
      <h1>Home</h1>
      {/* 変数関数なので()をつける必要はない */}
      <button onClick={apiPractice}>API発動</button>
    </div>
  );
};
```
これはGithubのAPIを叩くコードであるが、まずfetch関数でAPIにアクセスする。
then関数とは非同期処理の関数で裏で粛々とAPIから帰ってきたレスポンスを扱っていますという意味である。
ここではJSON形式のデータを取り出すため、responseに対してjson関数を使っている。
そして、このjson関数も非同期処理であるのでthen関数を使って結果をコンソールで打ち出している。

## ReactにおけるFormタグの利用
PHPでは絶対不可欠のFormタグもReactにおいては入力時に発火するイベントやStateの役割によって必ずしも使わなければならないということでもないが、やはりフォームの一斉送信を簡潔に行うには欠かせなものである。
```Page1DetailA.tsx
import { FormEventHandler } from "react";

// Formの扱い方
const handleSubmit: FormEventHandler<HTMLFormElement> = (event) => {
  event.preventDefault();
  const form = new FormData(event.currentTarget);
  // if文を使わずに値挿入する
  const firstName = form.get("firstName") || "";
  const lastName = form.get("lastName") || "";
  alert(
    `FirstName: ${firstName}\nLastName: ${lastName}`
  );
};

export const Page1DetailA = () => {
  return (
    <div>
      <h1>DetailA</h1>
      <form onSubmit={handleSubmit}>
      <label>
        FirstName:
        <input type="text" name="firstName" defaultValue="" pattern="^[A-Za-z]+$" />
        <span className="error-message">
          FirstNameは英字で入力してください。
        </span>
      </label>
      <label>
        LastName:
        <input type="text" name="lastName" defaultValue="" pattern="^[A-Za-z]+$" />
        <span className="error-message">
          LastNameは英字で入力してください。
        </span>
      </label>
      <input type="submit" value="送信" />
    </form>
    </div>
  );
};
```
このようにSubmitを行なってform.get("フォーム名")で取得するというStateも使わず非常に簡単な形に実装できる。
ここでは付属機能としてリアルタイムバリデーションも実装している。
仕組みとしては<input>の入力値がpatternに沿わなければ<input>にinvalidが付与されることを利用してCSSを書き換える。
```App.css
.error-message {
  display: none;
  color: red;
  border: red solid 3px;
}
/* label:has(input:invalid)とはlabelクラスの中のinputがinvalidを持っていることを表す */
label:has(input:invalid) .error-message {
  display: block;
}
```
初めはerror-messageクラスを非表示にして、<input>にinvalidが付与されて初めて
```
{display: block;}
```
で顕在化させる仕組み。

## Reactフックの使い方
最後にReactの便利機能Stateをさらに簡単に使いやすくした**Reactフック**について簡単に解説する。

#### useStateについて
classやconstructorを使用しなくても簡単にStateを実装できるため、Reactフックの中で最も使用頻度が高い。
#### useEffectについて
レンダリング後に実行される、いわゆる「副作用クラス」。APIの起動など幅広い使い方がある。
#### useContextについて
ファイルやクラスごとの値の受け渡しに関してPropsを用いれば数が増えるにつれタコ足回線状態になり、非常に複雑である。それをContextという機能を使うことでわかりやすく実現できる。
#### useRefについて
本来ReactにおいてDOMの中身を編集することは少し面倒であるが、数行のコードでそれを行うことができる。

これらを実際のプログラムの中に実装していく。
```Page1.tsx
import { useState, useEffect, useContext, useRef } from "react";
// useContextするためには元ファイルから持ってくる
import { PersonalContext } from "../App";

type personalInfo = {
  name: string;
  age: number;
}

export const Page1 = () => {
  // Stateを簡単に利用するための関数
  const [ count, setCount ] = useState(0);
  const addCount = () => setCount(count + 1);
  // useEffectの第二引数をcountにしているため2回目以降はcountが更新される時に呼ばれる([]にすると初回のみ呼ばれる)
  useEffect(() => {
    console.log("レンダリング後に実行される")
  },[count]);
  // App.tsxで記述した値を引っ張ってこれる
  const personalInfo: personalInfo = useContext(PersonalContext);
  // ReactでDOMを編集するのは手間がかかるがRefを使えば簡単、引数にnullを指定しないとDOMにアクセスできない
  const divRef = useRef<HTMLInputElement>(null);
  const changeDivColor = () => {
    // divRef.currentがnullの場合の対策も打つ
    if (divRef.current){
      divRef.current.style.backgroundColor = 'red';
    }
  };

  return (
    <div>
      <h1>Page1</h1>
      <div>
       <p>カウント数：{count}</p>
       <button onClick={addCount}>+</button>
      </div>
      
      <div>
       <p>{personalInfo.name}</p>
       <p>{personalInfo.age}</p>
      </div>

      <div>
       <div ref={divRef}>これはサンプルのdivです</div>
       <button onClick={changeDivColor}>色を変更する</button>
      </div>
    </div>
  );
};
```
useStateを用いて関数の中で簡単にStateを利用できるようになるのは非常にありがたい。
useContextに関しては親ファイルであるApp.tsxに値の設定をしなければならない。
```diff_javascript
+ import React, { createContext } from 'react';

+ type personalInfo = {
+   name: string;
+   age: number;
+ }
+ const personalInfo: personalInfo = {
+   name: "mere",
+   age: 30,
+ };
// exportを忘れると呼び出し先のファイルでimportできない
+ export const PersonalContext = createContext(personalInfo);

class App extends React.Component {
  render(){

    return (
+     <PersonalContext.Provider value={personalInfo}>
       <Router>
        <div className="App">
         <Link to="/">Home</Link>
         <br />
         <Link to="/page1">Page1</Link>
         
         <Routing />
        </div>
       </Router>
+     </PersonalContext.Provider>
    )
  }
}

export default App;
```
ContextProviderで値の適用範囲を囲むことで機能する。
継承したい値はProviderに渡して、それを適用したい箇所でuseContextを用いて使う。

#### このようにページ遷移やAPIおよびReactフックを使うことでより自由度の高いReact開発を可能するため、是非取り組んで頂きたい。

次章
[React & TypeScript初学者のすゝめ 第四章](https://qiita.com/pigretyasushi/items/3806fd338a361d725b2d)