【初心者用】React中級編【備忘録】

# これは何？
最低限のReactの知識はあるけど、もっと詳しく知りたいと思って勉強した備忘録です。
何かの参考になれば幸いです。

使った教材↓
[Reactに入門した人のためのもっとReactが楽しくなるステップアップコース完全版(Udemy)](https://www.udemy.com/course/react_stepup/?couponCode=STEPUP20220629)



# 目次
[レンダリングの最適化](#レンダリングの最適化)
[様々なCSSのあて方](#様々なcssのあて方)
[ReactRouter](#reactrouter)
[AtomicDesign](#atomicdesign)
[グローバルなstate管理(context/recoil)](#グローバルなstate管理contextrecoil)
[React×TypeScript](#reacttypescript)
[カスタムフック](#カスタムフック)
[感想](#感想)






# レンダリングの最適化
### 再レンダリングが起きる条件
1. stateが更新されたコンポーネントは再レンダリング
1. propsが更新されたコンポーネントは再レンダリング
1. 再レンダリングされたコンポーネント配下の子コンポーネントは再レンダリング

### 不要な再レンダリングを回避する方法1
`memo`を使用する（[公式：memo](https://ja.reactjs.org/docs/react-api.html#reactmemo)）

```react
// 親コンポーネント
import React, { useState } from 'react';
import { ChildArea } from './ChildArea';

export const Render = () => {
    const [text, setText] = useState('');
    const [open, setOpen] = useState(false);

    const onChangeText = (e) => setText(e.target.value);
    const onClickOpen = () => setOpen(!open);

    return (
        <>
            <input value={text} onChange={onChangeText}/>
            <br/>
            <br/>
            <button onClick={onClickOpen}>表示</button>
            <ChildArea open={open}/>
        </>
    );
};
```
```react
// 子コンポーネント
import React, { memo } from "react";

export const ChildArea = memo((props) => {
    const { open } = props;

    return (
        <>
        {open ? (
            <div style={style}>
                <p>子コンポーネント</p>
            </div>
        ) : null}
        </>
    );
});
```
:::note info
子コンポーネントを`memo`で囲ってあげることで、`props`の変更のみに関心を持つようになる。そのため、親要素が再レンダリングされたとしても、`props`が変更されない限り、子コンポーネントは再レンダリングされない。
:::


### 不要な再レンダリングを回避する方法2
`useCallback`を使用する（[公式：useCallback](https://ja.reactjs.org/docs/hooks-reference.html#usecallback)）

```react
// 親コンポーネント
import React, { useCallback, useState } from 'react';
import { ChildArea } from './ChildArea';

export const Render = () => {
    const [text, setText] = useState('');
    const [open, setOpen] = useState(false);

    const onChangeText = (e) => setText(e.target.value);
    const onClickOpen = () => setOpen(!open);
    const onClickClose = useCallback(() => setOpen(false), [setOpen]);

    return (
        <>
            <input value={text} onChange={onChangeText}/>
            <br/>
            <br/>
            <button onClick={onClickOpen}>表示</button>
            <ChildArea open={open} onClickClose={onClickClose}/>
        </>
    );
};
```
:::note info
callbackをメモ化する。アロー関数で生成した関数は、レンダリングされるたびに新しい関数として判定されるため、その関数(callback)を子コンポーネントに渡してた場合、変更がなくても子コンポーネントが再レンダリングされてしまう。`useCallback`を使用すると、第2引数に配列として渡したものにだけ関心を持つようになるため、配列の要素に変更がなければ同じ関数として判定されるようになる。
:::


### 不要な再レンダリングを回避する方法3
`useMemo`を使用する（[公式：useMemo](https://ja.reactjs.org/docs/hooks-reference.html#usememo)）
```react
const tmp = useMemo(() => 1 + 3, []);
```
:::note info
変数をmemo化する。配列が空の場合は最初の1回だけレンダリングする。
:::





# 様々なCSSのあて方
### InlineStyle
コンポーネントの中に直接cssを書く。書き方は`style={}`。
```react
export const InlineStyle = () => {
    const containerStyle = {
        display: 'flex',
        justifyContent: 'space-around',
        alignItems: 'center',
    };
    const titleStyle = {
        margin: 0,
        color: '#3d84a8',
    };
    const buttonStyle = {
        border: 'none',
        padding: '8px',
        borderRadius: '8px',
    };

    return (
        <div style={containerStyle}>
            <p style={titleStyle}>-- Inline Styles --</p>
            <button style={buttonStyle}>Fight!</button>
        </div>
    )
}
```
:::note info
react内でcssを記述する場合はローワーキャメルケースで書く
例：
border-radius ×
borderRadius ◯
:::

### CSS modules
cssファイル or Sassファイルを作成して、それを読み込む。書き方は`className={}`。
以下は、SASSファイルを読み込むやり方。
```scss
// CssModules.module.scss
.container {
    border: solid 2px #392eff;
    border-radius: 20px;
    padding: 8px;
    margin: 8px;
    display: flex;
    justify-content: space-around;
    align-items: center;
}

.title {
    margin: 0;
    color: #3d84a8;
}

.button {
    background-color: #abedd8;
    border: none;
    padding: 8px;
    border-radius: 8px;
    &:hover {
        background-color: #46cdcf;
        color: #fff;
        cursor: pointer;
    }
}
```
```react
// CssModules.jsx
import classes from "./CssModules.module.scss";

export const CssModules = () => {
    return (
        <div className={classes.container}>
            <p className={classes.title}>-- Css Modules --</p>
            <button className={classes.button}>Fight!</button>
        </div>
    )
}
```
:::note info
通常のcssファイルの書き方ができる
cssのファイル名は`xxx.modules.css`の形にする
scssでは`&:hover`のような疑似要素を使うことができる
クラス名はimportした要素にしか関係しないので、同じクラス名を使用しても競合しない
importする際の変数名（上記だとclasses）はなんでもOK
:::


### styled-JSX
javaScriptの中にcssを書く。ライブラリなので事前にインストールする必要あり。
Next.jsにはデフォルトで入っているためインストールする必要なし。

インストール方法
```
npm install --save styled-jsx
```

書き方 `className=""`
```react
export const StyledJsx = () => {
    return (
        <>
            <div className="container">
                <p className="title">-- Styled JSX --</p>
                <button className="button">Fight!</button>
            </div>
            <style jsx="true">{`
                .container {
                    border: solid 2px #392eff;
                    border-radius: 20px;
                    padding: 8px;
                    margin: 8px;
                    display: flex;
                    justify-content: space-around;
                    align-items: center;
                }
                .title {
                    margin: 0;
                    color: #3d84a8;
                }
                .button {
                    background-color: #abedd8;
                    border: none;
                    padding: 8px;
                    border-radius: 8px;
                }
            `}</style>
        </>
    );
};
```
:::note info
デフォルトでは`&:hover`のような疑似要素は使えない
Sassのプラグインをインストールすれば使えるようになる
:::


### styled-components
cssをコンポーネントのように書く。ライブラリのため事前インストールが必要。

インストール方法
```
npm install --save styled-components
```

書き方
```react
import styled from "styled-components";

export const StyledComponents = () => {
    return (
        <SContainer>
            <STitle>-- StyledComponents JSX --</STitle>
            <SButton>Fight!</SButton>
        </SContainer>
    );
};

const SContainer = styled.div`
    border: solid 2px #392eff;
    border-radius: 20px;
    padding: 8px;
    margin: 8px;
    display: flex;
    justify-content: space-around;
    align-items: center;
`;
const STitle = styled.p`
    margin: 0;
    color: #3d84a8;
`;
const SButton = styled.button`
    background-color: #abedd8;
    border: none;
    padding: 8px;
    border-radius: 8px;
    &:hover {
        background-color: #46cdcf;
        color: #fff;
        cursor: pointer;
    }
`;
```
:::note info
`&:hover`などの疑似要素は使える
:::
:::note warn
他のクラスからインポートしたコンポーネントなのか、cssコンポーネントなのかの見分けがつかないので、区別する方法を考える必要がある
:::

### emotion
色々な書き方ができるライブラリ。TypeScriptと相性が良いらしい。

インストール方法
```
npm i @emotion/styled @emotion/react
```

書き方 `css={}` or コンポーネント
```react
/** @jsxRuntime classic */
/** @jsx jsx */
import { jsx, css } from "@emotion/react";
import styled from "@emotion/styled";

export const Emotion = () => {
    const containerStyle = css`
        border: solid 2px #392eff;
        border-radius: 20px;
        padding: 8px;
        margin: 8px;
        display: flex;
        justify-content: space-around;
        align-items: center;
    `;
    const titleStyle = css({
        margin: 0,
        color: "#3d84a8",
    });
    return (
        <div css={containerStyle}>
            <p css={titleStyle}>-- Emotion JSX --</p>
            <SButton>Fight!</SButton>
        </div>
    );
};

const SButton = styled.button`
    background-color: #abedd8;
    border: none;
    padding: 8px;
    border-radius: 8px;
    &:hover {
        background-color: #46cdcf;
        color: #fff;
        cursor: pointer;
    }
`;
```
:::note info
`/** @jsx jsx */`は、jsx を React.createElement ではなく jsx という関数への呼び出しに変換するよう、babel に指示しているため、必須。ないと機能しない。
普通のcssのように書くこともできるし、コンポーネントとして書くこともできる
:::








# ReactRouter
Reactでルーティング処理（ページ遷移）を簡単に記述できるようになるライブラリ。
[こちらの記事](https://xn--eckhu0e2b3a6i6dsh.net/react-router-dom/)がとても分かりやすかったので、参考にさせていただきました。

インストール方法
```
npm add react-router-dom@6
```

### Routingの基本的な書き方

#### BrowserRouter
routerを使うために最初に記述する必要があるもの。
```react
import { render } from "react-dom";
import App from "./App";

// react-router-domからBrowserRouterをimportする。
import { BrowserRouter } from "react-router-dom";

render(
  // BrouserRouerタグでAppタグを囲む。
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById("root")
);
```
:::note info
Routerを使えるようにするために、必須
:::

#### Routes, Route, Link
Routeのpathとelementを設定することでURLを指定するとそのページに遷移ができるようになる。
またLinkタグを使うことでリンクを設定できる。
```react
import { Routes, Route, Link } from "react-router-dom";
import { Home } from "./components/Home";
import { About } from "./components/About";
import { Contact } from "./components/Contact";

export default function App() {
  return (
    <>
      <nav>
        <ul>
          <li><Link to="/">HOME</Link></li>
          <li><Link to="/about">ABOUT</Link></li>
          <li><Link to="/contact">Contact</Link></li>
        </ul>
      </nav>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/contact" element={<Contact />} />
      </Routes>
    </>
  );
}
```
:::note info
`Route`のタグを使う場合は`Routes`で囲ってあげる必要がある
`path`はURLでの表記、`element`は表示するコンポーネント
:::


#### NavLink
Linkタグの特別バージョンが NavLink。
現在のURLとリンクのpath属性の値が一致している(アクティブかどうか)を判定できる。
```react
// style属性を使った場合
<NavLink to="path" style={({ isActive }) => (isActive ? activeStyle : {})}

// className属性を使った場合
<NavLink to-"path" className={({ isActive }) => (isActive ? "activated" : "")} 
```

### 404ページへのリダイレクト
`*`を使うことで、いずれにも一致しない`path`だった場合のリダイレクト先を指定できる
```react
<Route path='*' element={<Page404 />}/>
```

### 親ルートの下に子ルートを作成する
`outlet`を使うことで子ルートの内容を表示させることができる
```react
// 子ルートの設定
<Routes>
    {/* Routeタグの内部にRouteタグを設置して、入れ子構造にする */}
    <Route path="/" element={<User />}>
        {/* 子側のRouteのpath属性は、親のRouteタグのpathから見た相対パスを設定する */}
        <Route path="yamada" element={<Yamada />} />
        <Route path="tanaka" element={<Tanaka />} />
    </Route>
</Routes>
```
```react
// Userコンポーネントの作成
import React from "react";
import { Alert } from "react-bootstrap";
// react-router-domライブラリから、Outletをimportする
import { Outlet } from "react-router-dom";


export const User: React.VFC = () => {
  return (
    <Alert variant="primary">
      <h1>User</h1>
      {/* Outletタグを設置 */}
      {/* 子側のRouteタグのelement属性のコンポーネントが差し込まれる目印になる */}
      <Outlet />
    </Alert>
  );
};
```
:::note info
`Route`タグの内部に`Route`タグを設置すると子ルートになる
`Outlet`を使うことで子コンポーネントの要素を表示できる
:::

### リダイレクトさせる（useNavigate）
useNavigateを使用することでリダイレクトさせることができる
```react
// react-router-domから、useNavigateフックをimportする。
import { useNavigate } from "react-router-dom";

// いったん、navigateメソッドを生成する。
const navigate = useNavigate();

// 第1引数には、リダイレクト先の相対パスを指定。
// 第2引数には、必要に応じてstateとreplaceを指定。
navigate("relative path", { state: { key1: value1, key2: value2 }, replace: booleran });
```
:::note info
replace:trueの場合リダイレクト元ページの参照を履歴に残さない(ブラウザの戻るは使えない)
`navigate(-1)`にすると戻るボタンを実装できる
:::

### パスパラメータを取得する（useParams）
path属性で「:」から始まる文字列を設定したものをuseParamsで取得できる
```react
// 呼び出す側でidをパスに指定
<Route path=':id' element={<UserParameter />} />
```
```react
import { useParams } from "react-router-dom";

export const UserParameter = () => {
    let { id } = useParams();
    return (
        <>
            <p>UserParameterページです</p>
            <p>パスパラメータは { id } です</p>
        </>
    );
};
```
:::note info
uriに`xxx/id`(パスパラメータ)が含まれていた場合、上記でそのidを取得できる
:::

### クエリ文字列（クエリパラメータ）を取得する（useSearchParams or URLSearchParams）
クエリ文字列を取得する場合は、Reactの場合、`useSearchParams`を使う。
もしくは、Reactの`useLocation` + WebAPIの`URLSearchParams`を使う。

#### useSearchParamsを使う書き方
```react
// react-router-domライブラリから、useSearchParamsフックをimportする
import { useSearchParams } from "react-router-dom";

// searchParamsオブジェクトとsetterメソッドを生成する
const [searchParams, setSearchParams] = useSearchParams();

// searchParamsオブジェクトのgetメソッドにkeyを渡す
// クエリパラメーターが「?id=12345」なら、param には「12345」が格納される
const param = searchParams.get("id")
```
:::note info
`searchParams.get("xxx")`でクエリ文字列を取得できる
:::

#### useLocation + URLSearchParamsを使う書き方
```react
import { useLocation } from "react-router-dom";

export const UserParameter = () => {
    let { search } = useLocation();
    const query = new URLSearchParams(search);
    return (
        <>
            <p>クエリパラメータは { query.get('name') } です</p>
        </>
    );
};
```
:::note info
`useLocation`は現在地のオブジェクトを返す
searchにはクエリパラメータが入っている
`URLSearchParams`をインスタンス化する際の引数にsearchを渡す
:::

### stateを渡すページ遷移
Linkにstateを記述する
```react
import { Link } from "react-router-dom";

export const Page1 = () => {
    const arr = [...Array(100).keys()];

    return (
        <div>
            <h1>Page1ページです</h1>
            <Link to={`detailA`} state={ arr }>DetailA</Link>
        </div>
    );
};
```
```react
import { useLocation } from "react-router-dom";

export const Page1DetailA = () => {
    // 渡される側はuseLocationを使う
    const { state } = useLocation();
    console.log(state);

    return (
        <div>
            <h1>Page1DetailAページです</h1>
        </div>
    );
};
```
:::note info
`Link state={{ xxx: "yyy" }}`の形でstateを渡すことができる
渡される側は`useLocation`から`state`を取り出すことで使うことができる
:::






# AtomicDesign

AtomicDesignとは…
1. BradFrost氏が考えたデザインシステム
1. 画面要素を5段階に分けて、組み合わせることでUIを実現する
1. コンポーネント化された要素が画面を組み合わせているという考え方
1. React用,Vue用というわけではない
1. モダンjavaScriptと相性が良い

5段階のコンポーネントとは…
1. Atoms（原子）→最も小さい単位。　例：ボタン単体
1. Molecules（分子）→Atomを組み合わせたもの。　例：アイコン＋メニュー名
1. Organisms（有機体）→AtomやMoleculeの組み合わせ。　例：サイドメニュー
1. Templates（テンプレート）→ページのレイアウトだけ。データは持たない。
1. Pages（ページ）→最終的に表示される1画面。

参照：[アトミックデザインとは？メリットや気を付けるポイントを徹底解説！](https://spice-factory.co.jp/web/about-atmicdesign/)

:::note info
AtomicDesignはあくまで概念
最初から分けようとせず、定期的にリファクタリングすることで対応させていく
要素の関心を意識する
:::






# グローバルなstate管理(context/recoil)

グローバルなstateとは、どの画面からでも参照、更新ができる値のこと。
useStateを使った場合は、コンポーネント内だけで完結してしまうため、他のコンポーネントに渡す場合は、propsを使っていくしかない。しかし、規模が大きくなればなるほど、複雑になってしまうため、グローバルなstateを使用する。

### createContext, useContextを使う
```react
// UserProvider.jsxに記述
import { createContext, useState } from "react";

export const UserContext = createContext({});

export const UserProvider = (props) => {
    const { children } = props;
    const [userInfo, setUserInfo] = useState(null);

    return(
        <UserContext.Provider value={{ userInfo, setUserInfo }}>
            {children}
        </UserContext.Provider>
    )
}
```
:::note info
`createContext`でcontextを作成する
`createContext`の引数はデフォルト値だが、空のオブジェクトを渡せばOK
作成したcontextにはProviderコンポーネントが付属している
`<UserContext.Provider value={/* 何らかの値 */}>`で子孫のコンポーネントにvalueを渡すことができる
Providerのvalueが変更されるたびに子孫のコンポーネントも再レンダリングされる
:::

```react
// UserProviderを呼び出す
import { Outlet } from "react-router-dom";
import { UserProvider } from "./providers/UserProvider";

export const AtomicDesign = () => {
    return (
        <UserProvider>
            <Outlet/>
        </UserProvider>
    );
};
```
```react
// AtomicDesignの下にあるUsers.jsx
import { HeaderOnly } from "../templates/HeaderOnly";
import { SearchInput } from "../molcules/SearchInput";
import { SecondaryButton } from "../atoms/button/SecondaryButton";
import { UserContext } from "../../../providers/UserProvider";
import { useContext } from "react";

export const Users = () => {
    const { userInfo, setUserInfo } = useContext(UserContext);
    const onClickSwitch = () => setUserInfo({ isAdmin: !userInfo.isAdmin });

    return (
        <HeaderOnly>
            <h2>ユーザー一覧</h2>
            <SearchInput />
            <br/>
            <SecondaryButton onClick={onClickSwitch}>切り替え</SecondaryButton>
        </HeaderOnly>
    );
};
```
:::note info
contextを使用するには`useContext`を使う
`useContext`の引数には、作成したcontextを渡す
:::
:::note warn
もし孫のコンポーネントがある場合には、不要な再レンダリングを避けるためには`memo`で孫コンポーネントを囲う
:::


### Recoilを使う

Recoilはstateを管理するためのライブラリ。

recoilをインストール
```
npm install recoil
```

Recoilを使う
```react
// グローバルなstateを定義
import { atom } from "recoil";

export const userState = atom({
    key: "userState",
    default: { isAdmin: false }
});
```
:::note info
`recoil`の中の`atom`を使用する
defaultにはデフォルト値を指定する
:::

```react
// Recoilを使用するコンポーネントの親コンポーネントをRecoilRootで囲う
import { Outlet } from "react-router-dom";
import { RecoilRoot } from "recoil";

export const AtomicDesign = () => {
    return (
        <RecoilRoot>
            <Outlet/>
        </RecoilRoot>
    );
};
```
:::note info
`RecoilRoot`で囲う
:::

```react
// 定義したuserStateを使用する

import { HeaderOnly } from "../templates/HeaderOnly";
import { SearchInput } from "../molcules/SearchInput";
import { SecondaryButton } from "../atoms/button/SecondaryButton";
import { useRecoilState } from "recoil";
import { userState } from "../../../store/userState";

export const Users = () => {
    const [userInfo, setUserInfo] = useRecoilState(userState);
    const onClickSwitch = () => setUserInfo({ isAdmin: !userInfo.isAdmin });

    return (
        <HeaderOnly>
            <h2>ユーザー一覧</h2>
            <SearchInput />
            <br/>
            <SecondaryButton onClick={onClickSwitch}>切り替え</SecondaryButton>
        </HeaderOnly>
    );
};
```
:::note info
`useRecoilState(userState)`でグローバルなstateを使用する
`useState`と同じように、第1引数にstateの値、第2引数にstateを更新する値を取る
:::

```react
const userInfo = useRecoilValue(userState);
```
:::note info
stateだけ使用する場合は`useRecoilValue()`を使用する
:::

```react
const setUserInfo = useSetRecoilState(userState);
```
:::note info
更新する値だけを使用したい場合は`useSetRecoilState()`を使用する
:::






# React×TypeScript
TypeScriptと併せて開発するとエラーが起きづらいシステムになるので、TypeScriptを使うケースを見てみる。

### React×TypeScriptのプロジェクトの作成
```
npx create-react-app --template typesciprt my-app
```
:::note info
`--template typesciprt`を加えることでTypeCciprtのプロジェクトが作成できる
:::

### 基本的な型
```typescript
// boolean
let bool: boolean = true;

// number
let num: number = 0;

// string
let str: string = 'hoge';

// Array
let arr1: Array<number> = [0, 1, 2];
let arr2: number[] = [0, 1, 2];

// tuple
let tuple: [number, string] = [0, 'hoge'];

// any どんな型でもOK, なるべく使わない
let any: any = false;

// void (戻り値はあえて記述しなくてもTypeScriptが勝手に判定してくれる)
const funcA = (): void => {
    const test = 'TEST';
}

// null
let null1: null = null;

// undefined
let undefined1: undefined = undefined;

// object
let obj1: object = {};
let obj2: { id: number, name: string } = {id: 0, name: 'hoge'};
```
:::note info
変数名の後に`: 型名`で型を定義できる
:::

### 引数、返却値（戻り値）に型を指定
```typescript
const calcTotalFee = (num: number): number => {
    return const total = num * 1.1;
}
```
:::note info
引数、返り値にも型を定義できる
:::

### 設定ファイル（tsconfig.json）の変更
```typescript
{
    "compilerOptions": {

    // strictモードの設定
    "strict": true,

    // anyのみの設定
    "noImplicitAny": false,
    }
}
```
:::note info
`"strcit": true`の場合、型定義など厳格に判断する
`"noImplicitAny":`はanyの型のみ判定する。falseの場合、anyを許容する
:::

### typeで新しい型を定義する
```typescript
export type TodoType = {
    userId: number;
    id: number;
    title: string;
    completed: boolean;
};
```
```typescript
// 作成した型を使用する1
const [todos, setTodos] = useState<Array<TodoType>>([]);

// 作成した型を使用する2
axios.get<Array<TodoType>>('https://jsonplaceholder.typicode.com/todos').then((result) => {
    setTodos(result.data);
});
```
:::note info
`type`を使用することで新しい型を定義できる
`useState`に型を定義する場合には`userState<型>`の形にする
`<Array<TodoType>>`でarrayの中の型を定義している
`axios`に型を定義する場合には`axios.get`の後に型を記述する
:::

### porpsに型を定義する
```typescript
type TodoType = {
    userId: number;
    title: string;
    completed?: boolean;
};

export const Todo = (props: TodoType) => {
    const { title, userId, completed = false } = props;
    // 省略
}
```
:::note info
`props: 型名`で型を定義できる
必須ではない値には`?`を付ける
必須ではない値はデフォルト値を設定しておく(`completed = false`)
:::

### 共通の型定義を使用する（Pick, Omit）
共通の型を使用する場合、使うキーを指定するPickと、使わないキーを指定するOmitが便利。
```typescript
// src/types/todo.ts
export type TodoType = {
    userId: number;
    id: number;
    title: string;
    completed: boolean;
};
```
```typescript
// Pickを使うケース
import { TodoType } from "./types/todo";

export const Todo = (
    props: Pick<TodoType, "title" | "userId" | "completed">
    ) => {
    const { title, userId, completed = false } = props;
    // 省略
}
```
```typescript
// Omitを使うケース
import { TodoType } from "./types/todo";

export const Todo = (props: Omit<TodoType, "id">) => {
    const { title, userId, completed = false } = props;
    // 省略
}
```
:::note info
`Pick<型名、"使用するキー">`の形で型を指定することができる
`Omit<型名、"使用しないキー">`の形で型を指定することができる
:::

### 関数コンポーネント自体に型を定義する
```typescript
import { FC } from "react";

type Props = {
    color: string;
    fontSize: string;
}

export const Text: FC<Props> = (props) => {
    const { color, fontSize } = props;
    return <p style={{ color, fontSize }}>テキストです</p>
};
```
:::note info
`FC<型名>`で関数コンポーネントに型を定義できる
react v17以前の場合には`FC`ではなく`VFC`を推奨（暗黙的にchildrenを含むため）
:::

### オプショナルチェイニング
要素をつなげていく時に、途中の要素がない場合にundefinedを返すようにする
```typescript
<dd>{user.hobbies?.join(' / ')}</dd>
```
:::note info
hobbiesの後に`?`を付けることで、hobbiesがなかったらその時点でundefinedを返す
?がないとjoinでエラーになる場合がある
`join`は受け取った配列の全要素を連結した新たな文字列を返すメソッド
:::

### ライブラリの型定義
ライブラリ自体にも型の定義があり、インストールする必要がある場合がある

例：react-router-domの型定義をインストールする
```
npm install --save @types/react-router-dom
```
:::note info
axiosのようにライブラリ自体に型定義を包含しているものは型定義のインストールは不要
ライブラリのgithubに`index.d.ts`というファイルがあれば型定義がある
:::






# カスタムフック
カスタムフックとは...
1. 関数のこと
1. コンポーネントからロジックを切り離すために使う
1. 「use~」という名前で自由に作れる

### カスタムフックの作成
```typescript
// useAllUsers.tsとして作成

import axios from "axios";
import { useState } from "react";
import { User } from "../types/api/user";
import { UserProfile } from "../types/userProfile";

// 全ユーザー一覧を取得するカスタムフック
export const useAllUsers = () => {
    const [userProfiles, setUserProfiles] = useState<Array<UserProfile>>([]);
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState(false);

    const getUsers = () => {
        setLoading(true);
        setError(false);

        axios.get<Array<User>>('https://jsonplaceholder.typicode.com/users')
        .then((result) => {
        const data = result.data.map((user) => ({
            id: user.id,
            name: `${user.name}(${user.username})`,
            email: user.email,
            address: `${user.address.city}${user.address.suite}${user.address.street}`,
        }));
        setUserProfiles(data);
        })
        .catch(() => {
        setError(true);
        })
        .finally(() => {
        setLoading(false);
        });
    };

    return { getUsers, userProfiles, loading, error };
}
```
:::note info
`use~`という名前で作成する
使いたい関数や変数をreturnする
:::

### カスタムフックを呼び出す
```typescript
import { useAllUsers } from './hooks/useAllUsers';

function App() {
  const { getUsers, userProfiles, loading, error } = useAllUsers();
  const onClickFetchUser = () => getUsers();

  return (
    <div className="App">
      <button onClick={onClickFetchUser}>データ取得</button>
      <br/>
      {error ? (
        <p style={{ color:'red' }}>データの取得に失敗しました</p>
      ) : loading ? (
        <p>Loading...</p>
      ) : (
        <>
          {userProfiles.map((user) => (
            <UserCard key={user.id} user={user}/>
          ))}
        </>
      ) }
    </div>
  );
}
```
:::note info
`useAllUsers()`で作成したカスタムフックを呼び出している
JavaScriptの分割代入でreturnした関数や変数を使用できる
:::





# 感想
思った以上に幅広く勉強できたんじゃないかと思います。
フロントも楽しいなと思える内容でした。
今後はcssフレームワークやnext.jsにも手を出していきたいです。