0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Last updated at Posted at 2022-08-05

これは何?

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

使った教材↓
Reactに入門した人のためのもっとReactが楽しくなるステップアップコース完全版(Udemy)

目次

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

レンダリングの最適化

再レンダリングが起きる条件

  1. stateが更新されたコンポーネントは再レンダリング
  2. propsが更新されたコンポーネントは再レンダリング
  3. 再レンダリングされたコンポーネント配下の子コンポーネントは再レンダリング

不要な再レンダリングを回避する方法1

memoを使用する(公式:memo

// 親コンポーネント
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}/>
        </>
    );
};
// 子コンポーネント
import React, { memo } from "react";

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

    return (
        <>
        {open ? (
            <div style={style}>
                <p>子コンポーネント</p>
            </div>
        ) : null}
        </>
    );
});

子コンポーネントをmemoで囲ってあげることで、propsの変更のみに関心を持つようになる。そのため、親要素が再レンダリングされたとしても、propsが変更されない限り、子コンポーネントは再レンダリングされない。

不要な再レンダリングを回避する方法2

useCallbackを使用する(公式:useCallback

// 親コンポーネント
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}/>
        </>
    );
};

callbackをメモ化する。アロー関数で生成した関数は、レンダリングされるたびに新しい関数として判定されるため、その関数(callback)を子コンポーネントに渡してた場合、変更がなくても子コンポーネントが再レンダリングされてしまう。useCallbackを使用すると、第2引数に配列として渡したものにだけ関心を持つようになるため、配列の要素に変更がなければ同じ関数として判定されるようになる。

不要な再レンダリングを回避する方法3

useMemoを使用する(公式:useMemo

const tmp = useMemo(() => 1 + 3, []);

変数をmemo化する。配列が空の場合は最初の1回だけレンダリングする。

様々なCSSのあて方

InlineStyle

コンポーネントの中に直接cssを書く。書き方はstyle={}

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>
    )
}

react内でcssを記述する場合はローワーキャメルケースで書く
例:
border-radius ×
borderRadius ◯

CSS modules

cssファイル or Sassファイルを作成して、それを読み込む。書き方はclassName={}
以下は、SASSファイルを読み込むやり方。

// 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;
    }
}
// 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>
    )
}

通常のcssファイルの書き方ができる
cssのファイル名はxxx.modules.cssの形にする
scssでは&:hoverのような疑似要素を使うことができる
クラス名はimportした要素にしか関係しないので、同じクラス名を使用しても競合しない
importする際の変数名(上記だとclasses)はなんでもOK

styled-JSX

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

インストール方法

npm install --save styled-jsx

書き方 className=""

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>
        </>
    );
};

デフォルトでは&:hoverのような疑似要素は使えない
Sassのプラグインをインストールすれば使えるようになる

styled-components

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

インストール方法

npm install --save styled-components

書き方

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;
    }
`;

&:hoverなどの疑似要素は使える

他のクラスからインポートしたコンポーネントなのか、cssコンポーネントなのかの見分けがつかないので、区別する方法を考える必要がある

emotion

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

インストール方法

npm i @emotion/styled @emotion/react

書き方 css={} or コンポーネント

/** @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;
    }
`;

/** @jsx jsx */は、jsx を React.createElement ではなく jsx という関数への呼び出しに変換するよう、babel に指示しているため、必須。ないと機能しない。
普通のcssのように書くこともできるし、コンポーネントとして書くこともできる

ReactRouter

Reactでルーティング処理(ページ遷移)を簡単に記述できるようになるライブラリ。
こちらの記事がとても分かりやすかったので、参考にさせていただきました。

インストール方法

npm add react-router-dom@6

Routingの基本的な書き方

BrowserRouter

routerを使うために最初に記述する必要があるもの。

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")
);

Routerを使えるようにするために、必須

Routes, Route, Link

Routeのpathとelementを設定することでURLを指定するとそのページに遷移ができるようになる。
またLinkタグを使うことでリンクを設定できる。

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>
    </>
  );
}

Routeのタグを使う場合はRoutesで囲ってあげる必要がある
pathはURLでの表記、elementは表示するコンポーネント

NavLink

Linkタグの特別バージョンが NavLink。
現在のURLとリンクのpath属性の値が一致している(アクティブかどうか)を判定できる。

// style属性を使った場合
<NavLink to="path" style={({ isActive }) => (isActive ? activeStyle : {})}

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

404ページへのリダイレクト

*を使うことで、いずれにも一致しないpathだった場合のリダイレクト先を指定できる

<Route path='*' element={<Page404 />}/>

親ルートの下に子ルートを作成する

outletを使うことで子ルートの内容を表示させることができる

// 子ルートの設定
<Routes>
    {/* Routeタグの内部にRouteタグを設置して、入れ子構造にする */}
    <Route path="/" element={<User />}>
        {/* 子側のRouteのpath属性は、親のRouteタグのpathから見た相対パスを設定する */}
        <Route path="yamada" element={<Yamada />} />
        <Route path="tanaka" element={<Tanaka />} />
    </Route>
</Routes>
// 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>
  );
};

Routeタグの内部にRouteタグを設置すると子ルートになる
Outletを使うことで子コンポーネントの要素を表示できる

リダイレクトさせる(useNavigate)

useNavigateを使用することでリダイレクトさせることができる

// 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 });

replace:trueの場合リダイレクト元ページの参照を履歴に残さない(ブラウザの戻るは使えない)
navigate(-1)にすると戻るボタンを実装できる

パスパラメータを取得する(useParams)

path属性で「:」から始まる文字列を設定したものをuseParamsで取得できる

// 呼び出す側でidをパスに指定
<Route path=':id' element={<UserParameter />} />
import { useParams } from "react-router-dom";

export const UserParameter = () => {
    let { id } = useParams();
    return (
        <>
            <p>UserParameterページです</p>
            <p>パスパラメータは { id } です</p>
        </>
    );
};

uriにxxx/id(パスパラメータ)が含まれていた場合、上記でそのidを取得できる

クエリ文字列(クエリパラメータ)を取得する(useSearchParams or URLSearchParams)

クエリ文字列を取得する場合は、Reactの場合、useSearchParamsを使う。
もしくは、ReactのuseLocation + WebAPIのURLSearchParamsを使う。

useSearchParamsを使う書き方

// 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")

searchParams.get("xxx")でクエリ文字列を取得できる

useLocation + URLSearchParamsを使う書き方

import { useLocation } from "react-router-dom";

export const UserParameter = () => {
    let { search } = useLocation();
    const query = new URLSearchParams(search);
    return (
        <>
            <p>クエリパラメータは { query.get('name') } です</p>
        </>
    );
};

useLocationは現在地のオブジェクトを返す
searchにはクエリパラメータが入っている
URLSearchParamsをインスタンス化する際の引数にsearchを渡す

stateを渡すページ遷移

Linkにstateを記述する

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>
    );
};
import { useLocation } from "react-router-dom";

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

    return (
        <div>
            <h1>Page1DetailAページです</h1>
        </div>
    );
};

Link state={{ xxx: "yyy" }}の形でstateを渡すことができる
渡される側はuseLocationからstateを取り出すことで使うことができる

AtomicDesign

AtomicDesignとは…

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

5段階のコンポーネントとは…

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

参照:アトミックデザインとは?メリットや気を付けるポイントを徹底解説!

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

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

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

createContext, useContextを使う

// 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>
    )
}

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

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

export const AtomicDesign = () => {
    return (
        <UserProvider>
            <Outlet/>
        </UserProvider>
    );
};
// 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>
    );
};

contextを使用するにはuseContextを使う
useContextの引数には、作成したcontextを渡す

もし孫のコンポーネントがある場合には、不要な再レンダリングを避けるためにはmemoで孫コンポーネントを囲う

Recoilを使う

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

recoilをインストール

npm install recoil

Recoilを使う

// グローバルなstateを定義
import { atom } from "recoil";

export const userState = atom({
    key: "userState",
    default: { isAdmin: false }
});

recoilの中のatomを使用する
defaultにはデフォルト値を指定する

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

export const AtomicDesign = () => {
    return (
        <RecoilRoot>
            <Outlet/>
        </RecoilRoot>
    );
};

RecoilRootで囲う

// 定義した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>
    );
};

useRecoilState(userState)でグローバルなstateを使用する
useStateと同じように、第1引数にstateの値、第2引数にstateを更新する値を取る

const userInfo = useRecoilValue(userState);

stateだけ使用する場合はuseRecoilValue()を使用する

const setUserInfo = useSetRecoilState(userState);

更新する値だけを使用したい場合はuseSetRecoilState()を使用する

React×TypeScript

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

React×TypeScriptのプロジェクトの作成

npx create-react-app --template typesciprt my-app

--template typesciprtを加えることでTypeCciprtのプロジェクトが作成できる

基本的な型

// 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'};

変数名の後に: 型名で型を定義できる

引数、返却値(戻り値)に型を指定

const calcTotalFee = (num: number): number => {
    return const total = num * 1.1;
}

引数、返り値にも型を定義できる

設定ファイル(tsconfig.json)の変更

{
    "compilerOptions": {

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

    // anyのみの設定
    "noImplicitAny": false,
    }
}

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

typeで新しい型を定義する

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

// 作成した型を使用する2
axios.get<Array<TodoType>>('https://jsonplaceholder.typicode.com/todos').then((result) => {
    setTodos(result.data);
});

typeを使用することで新しい型を定義できる
useStateに型を定義する場合にはuserState<型>の形にする
<Array<TodoType>>でarrayの中の型を定義している
axiosに型を定義する場合にはaxios.getの後に型を記述する

porpsに型を定義する

type TodoType = {
    userId: number;
    title: string;
    completed?: boolean;
};

export const Todo = (props: TodoType) => {
    const { title, userId, completed = false } = props;
    // 省略
}

props: 型名で型を定義できる
必須ではない値には?を付ける
必須ではない値はデフォルト値を設定しておく(completed = false)

共通の型定義を使用する(Pick, Omit)

共通の型を使用する場合、使うキーを指定するPickと、使わないキーを指定するOmitが便利。

// src/types/todo.ts
export type TodoType = {
    userId: number;
    id: number;
    title: string;
    completed: boolean;
};
// Pickを使うケース
import { TodoType } from "./types/todo";

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

export const Todo = (props: Omit<TodoType, "id">) => {
    const { title, userId, completed = false } = props;
    // 省略
}

Pick<型名、"使用するキー">の形で型を指定することができる
Omit<型名、"使用しないキー">の形で型を指定することができる

関数コンポーネント自体に型を定義する

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>
};

FC<型名>で関数コンポーネントに型を定義できる
react v17以前の場合にはFCではなくVFCを推奨(暗黙的にchildrenを含むため)

オプショナルチェイニング

要素をつなげていく時に、途中の要素がない場合にundefinedを返すようにする

<dd>{user.hobbies?.join(' / ')}</dd>

hobbiesの後に?を付けることで、hobbiesがなかったらその時点でundefinedを返す
?がないとjoinでエラーになる場合がある
joinは受け取った配列の全要素を連結した新たな文字列を返すメソッド

ライブラリの型定義

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

例:react-router-domの型定義をインストールする

npm install --save @types/react-router-dom

axiosのようにライブラリ自体に型定義を包含しているものは型定義のインストールは不要
ライブラリのgithubにindex.d.tsというファイルがあれば型定義がある

カスタムフック

カスタムフックとは...

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

カスタムフックの作成

// 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 };
}

use~という名前で作成する
使いたい関数や変数をreturnする

カスタムフックを呼び出す

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>
  );
}

useAllUsers()で作成したカスタムフックを呼び出している
JavaScriptの分割代入でreturnした関数や変数を使用できる

感想

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

0
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?