まえがき
シリーズ形式の記事となります。今回は2つめ。
1. 【React】管理画面を作りながらReactを学ぶ(ChakraUI/コンポーネント化/react rooter v6)
2. 【React】管理画面を作りながらReactを学ぶ(ライフサイクル関数/props/dynamic routing)
3. 【React】管理画面を作りながらReactを学ぶ(ビルド)
前回の記事で作った管理画面
・レイアウト(ヘッダ/サイドメニュー)実装
・ルーティング(サイドメニューアイコンをクリックで画面切替)実装
今回の記事では「ライフサイクル関数」「props」を利用して、各画面が表示された時にそれっぽいデータをテーブル形式で表示してみる。
ライフサイクル
・DOMが生成されてから破棄されるまでの一連の流れのこと
・各タイミングで実行させたい処理を「ライフサイクル関数」内に定義する。
例1) Account画面表示する時に、Account一覧取得APIをコールして取得結果を画面に表示したい。
例2) stateで管理しているsearchKeywordが更新される度に検索処理をかけ直す。
「クラスコンポーネント」のライフサイクル関数
今回は関数コンポーネントで実装しているが、勉強がてらクラスコンポーネントのライフサイクル関数についてもMEMOしておく。
componentDidMount()
・Component が Mount された後に実行される。
・Ajax でデータフェッチ(初回)
・DOM に対する処理を行う(初回)
componentDidUpdate()
・Component の props または state が変更されたときに実行される。
・Ajax でデータフェッチ(2回目以降)
・DOM に対する処理を行う(2回目以降)
componentWillUnmount()
・Component が Unmount されるときに実行される。
・componentDidMount() で行った処理の解除を行うことが多い。
「関数コンポーネント」のライフサイクル関数
・componentDidMount()
・componentDidUpdate()
・componentWillUnmount()
は、関数コンポーネントだとuseEffect()
1つで済む。
useEffect()
構文
useEffect(() => {
/* 第1引数には実行させたい副作用関数を記述*/
console.log('副作用関数が実行されました!')
},[依存する変数の配列]) // 第2引数には副作用関数の実行タイミングを制御する依存データを記述
・第2引数を省略した場合、コンポーネントがレンダリングされるたびに、第1引数で渡した副作用関数が実行される=componentDidMount()扱い
useEffect()に渡す無名関数はasyncだと怒られるので、そうならないようにthen().catch()等で完結させる
Reactでデータ取得する方法についてまとまっているサイト
Account画面でデータ表示してみる。
今のAccount画面
今のsrc/routes/Account.tsx
export const Account = () => <h1>Account</h1>;
「Account」という文字だけ表示されている画面を、Account情報一覧が表示される感じにしたい。
やること
・画面表示時/componentDidMount()実行時 に、Account一覧取得APIをコールする。
・取得結果を画面に表示する。
APIサーバ構築するのはしんどいので、今回はaxiosでJSONファイルを返す。
yarn add axios
public/data/accounts.json を用意
[
{
"id": "act_0000001",
"name": "Anthony",
"age": 26,
"gender": "男"
},
{
"id": "act_0000002",
"name": "Bryant",
"age": 36,
"gender": "男"
},
{
"id": "act_0000003",
"name": "Carmelo",
"age": 35,
"gender": "男"
},
{
"id": "act_0000004",
"name": "Daisy",
"age": 21,
"gender": "女"
}
]
src/routes/Account.tsx を修正
// axiosをimport
import axios from "axios";
import { useEffect, useState } from "react";
import { Box, Table, Thead, Tbody, Tr, Th, Td } from "@chakra-ui/react";
// Account情報入れ物クラス
class AccountDto {
constructor(
public id = "",
public name = "",
public age = 0,
public gender = ""
) {}
}
export const Account = () => {
// アカウント情報格納用のstateを用意
const [accounts, setAccounts] = useState([]);
// useEffect()に渡す無名関数はasyncだと怒られるので、そうならないようにthen().catch()等で完結させる
useEffect(() => {
// publicフォルダ配下に対してHTTPリクエストを行うため「public/data/account.json」を取得
axios.get("/data/accounts.json").then((res) => {
setAccounts(res.data);
});
}, []);
return (
<div>
<Box m="10px">アカウント管理画面</Box>
<Table>
<Thead>
<Tr>
<Th>ID</Th>
<Th>NAME</Th>
<Th>AGE</Th>
<Th>GENDER</Th>
</Tr>
</Thead>
<Tbody>
{accounts.map((a: AccountDto) => {
return (
<Tr>
<Td>{a.id}</Td>
<Td>{a.name}</Td>
<Td>{a.age}</Td>
<Td>{a.gender}</Td>
</Tr>
);
})}
</Tbody>
</Table>
</div>
);
};
Account情報テーブルをコンポーネント化する&props利用
やること
・Account.tsxの<Table>~~</Table>
の部分をsrc/components/account/AccountTable.tsxとしてコンポーネント化する。
・親コンポーネント(Account.tsx)から子コンポーネント(AccountTable.tsx)へ値(Account一覧情報)をprops経由で渡す。
propsとは
・親コンポーネントから子コンポーネントへ値を渡すために使うパイプ的な役割なやつ
関数コンポーネントでのpropsの渡し方
渡す方(親コンポーネント)
Account.tsx
<AccountTable accounts={accounts} />
受け取る方(子コンポーネント)
AccountTable.tsx
class AccountDto {
constructor(
public id = "",
public name = "",
public age = 0,
public gender = ""
) {}
}
// propsとして受け取る型を定義
type Props = {
accounts: AccountDto[];
};
// 関数の引数経由でpropsを受け取る
export const AccountTable = (props: Props) => {
return (
<Table>
<Thead>
<Tr>
<Th>ID</Th>
<Th>NAME</Th>
<Th>AGE</Th>
<Th>GENDER</Th>
</Tr>
</Thead>
<Tbody>
{props.accounts.map((a: AccountDto) => {
return (
コンポーネント化&propsでアカウント情報一覧を渡してみた。
親:src/routes/Account.tsx
import axios from "axios";
import { useEffect, useState } from "react";
import { AccountTable } from "../components/account/AccountTable";
import { Box } from "@chakra-ui/react";
export const Account = () => {
const [accounts, setAccounts] = useState([]);
useEffect(() => {
axios.get("/data/accounts.json").then((res) => {
setAccounts(res.data);
});
}, []);
return (
<div>
<Box m="10px">アカウント管理画面</Box>
<AccountTable accounts={accounts} />
</div>
);
};
子:src/components/AccountTable.tsx
import { Table, Thead, Tbody, Tr, Th, Td } from "@chakra-ui/react";
class AccountDto {
constructor(
public id = "",
public name = "",
public age = 0,
public gender = ""
) {}
}
// propsとして受け取る型を定義
type Props = {
accounts: AccountDto[];
};
export const AccountTable = (props: Props) => {
return (
<Table>
<Thead>
<Tr>
<Th>ID</Th>
<Th>NAME</Th>
<Th>AGE</Th>
<Th>GENDER</Th>
</Tr>
</Thead>
<Tbody>
{props.accounts.map((a: AccountDto) => {
return (
<Tr>
<Td>{a.id}</Td>
<Td>{a.name}</Td>
<Td>{a.age}</Td>
<Td>{a.gender}</Td>
</Tr>
);
})}
</Tbody>
</Table>
);
};
アカウント詳細画面を作る
アカウント一覧テーブルの「詳細」ボタンを押すと、詳細画面が開かれるようにする。
dynamic routingの設定
詳細画面を開くときに、パラメータとしてアカウントIDを渡して、そのIDでアカウント情報取得APIをたたく&詳細画面に結果を表示する。
ルーティング設定追加
・<Route path="/account/:id" element={<AccountDetail />} />
を追加
export const App = () => {
return (
<ChakraProvider theme={theme}>
<Flex w="100vw" h="100wh">
<TopHeader />
<Box mt="100px">
<Flex>
<BrowserRouter>
<SideMenu />
<Box w="70vw">
<Routes>
<Route path="/" element={<Dashboard />} />
<Route path="/account/:id" element={<AccountDetail />} />
<Route path="/account" element={<Account />} />
<Route path="/charts" element={<Charts />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/mailBox" element={<MailBox />} />
<Route path="/message" element={<Message />} />
<Route path="/rateReview" element={<RateReview />} />
<Route path="/weather" element={<Weather />} />
</Routes>
</Box>
</BrowserRouter>
</Flex>
</Box>
</Flex>
</ChakraProvider>
);
};
アカウント詳細画面の作成
・パラメータに渡したIDをuseParams関数で取得する。 ※これもreact-router-dom v6のもの
import axios from "axios";
import { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import { Box } from "@chakra-ui/react";
class AccountDto {
constructor(
public id = "",
public name = "",
public age = 0,
public gender = ""
) {}
}
export const AccountDetail = () => {
const [account, setAccount] = useState(new AccountDto());
// パラメータ取得
const params = useParams();
useEffect(() => {
// IDをキーにアカウント情報取得
axios.get(`/data/account_${params.id}.json`).then((res) => {
setAccount(res.data);
});
/// useEffect関数の第2引数に空配列を指定=無限ループ防止
}, []);
return (
<Box m="20px">
<h1>ID:{account.id}</h1>
<h1>NAME:{account.name}</h1>
<h1>AGE:{account.age}</h1>
<h1>GENDER:{account.gender}</h1>
</Box>
);
};
アカウント情報テーブルの「詳細」ボタンをクリック→詳細画面に移動するようにする
・navigate("/account/${a.id}")
で詳細画面に移動
AccountTable.tsx
<Tbody>
{props.accounts.map((a: AccountDto) => {
return (
<Tr>
<Td>{a.id}</Td>
<Td>{a.name}</Td>
<Td>{a.age}</Td>
<Td>{a.gender}</Td>
<Td>
<Button
colorScheme="gray"
variant="ghost"
onClick={() => navigate(`/account/${a.id}`)}
>
<Text m="10px">詳細</Text>
</Button>
</Td>
</Tr>
);
})}
</Tbody>