[React] 初心者講座03 <Componentの役割分担+Routing処理>
目標
- 画面遷移するボタンを実装できる
- viewsディレクトリの階層ごとのComponentを実装できる
共通ヘッダの実装
こんな感じのヘッダ実装を例に説明する。
メニューボタンについては、ハンバーガーメニューやタブが一般的な気もするが、簡易的にボタンをそのまま表示することにした。
ヘッダはどのページでも表示させたいのでenvironments
にある<Home />
が表示を担う。
実装手順のイメージは以下
-
organisms
他アプリケーションでもコピペしたら使えるようなヘッダComponentを実装する -
ecosystems
(他のアプリでの共通化は意識せずに)上記Componentが必要とする値をpropsで与える -
environments
上記Componentを表示
organisms (AppHeader.tsx)
MUIのAppbarを使用する。
以下の例では、とりあえずReactNodeを受け取ってタイトルの横に並べるために、ReactNodeの配列をpropsに指定している。
ハンバーガーメニューを使用したヘッダにしたければ、ハンバーガーメニューを別のComponentに定義して、ここに表示し、メニューの項目名とクリックした時の処理をpropsで指定する。
そのときのpropsは、
menuItems: {label: string; onClick: () => void}[]
みたいになると思う。
{/* MUIのAppBarの色を変更 */}
const StyledAppbar = styled(AppBar)({
backgroundColor: blue[900],
});
export type AppHeaderProps = {
appTitle: string;
userName: string;
leftItems: { id: number; node: React.ReactNode }[];
};
const AppHeader: React.VFC<AppHeaderProps> = (props) => {
return (
<StyledAppbar position="static">
<Toolbar>
{/* タイトルを表示 */}
<AppTitleLabel label={props.appTitle} />
{/* 表示させたいものを並べるだけ */}
<Box display="flex" gap="8px" ml="8px">
{props.leftItems.map((item) => (
<React.Fragment key={item.id}>{item.node}</React.Fragment>
))}
</Box>
<Box display="flex" alignItems="center" gap="8px" ml="auto">
<AccountCircleIcon />
<Box>{props.userName}</Box>
</Box>
</Toolbar>
</StyledAppbar>
);
};
export default AppHeader;
ecosystems (CRAHeader.tsx)
organisms
にタイトルとReactNodeを並べれらるヘッダを作成した。
ecosystems
ではそのComponentに値を入れていく。
通信する処理の呼び出しはここ。
(今回、とりあえずユーザ名は固定値で書いた。)
アプリケーション専用のヘッダとするため、タイトルは固定値で与える。
さらに遷移用のボタンも設定する。
ただし、routingを担うのはenvironments
なのでクリック処理はpropsで受け取る。
export type CRAHeaderProps = Pick<DashboardButtonProps, 'onDashboardButtonClick'> &
Pick<DataListButtonProps, 'onDataListButtonClick'>;
const CRAHeader: React.VFC<CRAHeaderProps> = (props) => {
{/* 本来はuserNameをサーバから取得する */}
{/* そのうちjson-serverから取ることにする */}
const userName = 'User Name';
return (
<AppHeader
appTitle="CRA System"
userName={userName}
leftItems={[
{ id: 0, node: <DashboardButton onDashboardButtonClick={props.onDashboardButtonClick} /> },
{
id: 1,
node: <DataListButton onDataListButtonClick={props.onDataListButtonClick} />,
},
]}
/>
);
};
environments (Home.tsx)
routing処理を実装する。
react-router-dom
のuseRouteMatch()
で<Home />
のpathを取得し、それを元にuseHistory()
を使って、遷移したい画面のurlをpushすると遷移できる。
遷移処理を作る際に使用しているuseCallback
は簡単に説明すると、何回も同じ関数が生成されるのを防ぐもの。
Reactは再レンダリングが多く、普通のアロー関数だと毎回生成されてしまう。
第2引数の配列の値が変わったときだけ、関数も再生成される。
import { Route, Switch, Redirect, useRouteMatch, useHistory } from 'react-router-dom';
...
const Home: React.VFC = () => {
const { path } = useRouteMatch();
const history = useHistory();
const handleDashboardButtonClick = useCallback((): void => {
{/* history.push(遷移先のpath) */}
history.push(`${path}/dashboard`);
}, [history, path]);
const handleDataListButtonClick = useCallback((): void => {
history.push(`${path}/data-list`);
}, [history, path]);
return (
<AppContainer>
<CRAHeader
onDashboardButtonClick={handleDashboardButtonClick}
onDataListButtonClick={handleDataListButtonClick}
/>
<MainContainer>
<Switch>
...
ボタンComponentの実装
さらっと上のコードに書いてあったDashboardButton
とか細かい部品を実装する。
サブディレクトリは自由にしていい。
ボタンは多くなりそうなのでatoms/buttons
を作成しておいた。
atoms (HeaderMenuButton.tsx, DashboardButton.tsx)
まずベースとなるボタンを実装する。
atoms/buttons/bases
ディレクトリを作った。
見た目の異なるベースボタンはここに実装していくことにする。
ベースとなるボタンを元に様々なボタンを実装していく。
簡易版なのでdisable
とかloading
は無しで。
MUIのボタンを使用する。
propsはonClick
とボタン名のみ。
こうすることで、アプリケーション内のボタンのデザインを揃えることができる。
import React from 'react';
import Button, { ButtonProps } from '@mui/material/Button';
import { blueGrey } from '@mui/material/colors';
export type HeaderMenuButtonProps = Pick<ButtonProps, 'onClick'> & {
label: string;
};
const HeaderMenuButton: React.VFC<HeaderMenuButtonProps> = (props) => {
return (
<Button
variant="text"
onClick={props.onClick}
sx={{ color: blueGrey[200], fontWeight: 'bold' }}
>
{props.label}
</Button>
);
};
export default HeaderMenuButton;
先ほど実装したHeaderMenuButton
を使用してDashboardButton
を実装する。
以下が実装例。
propsは別名をつけてるけどPickでonClick
のままでもよい。
ボタンレベルでComponent化する理由は、このボタンの名前が変わったときに変更が楽だから。
import React from 'react';
import HeaderMenuButton, { HeaderMenuButtonProps } from './bases/HeaderMenuButton';
export type DashboardButtonProps = {
onDashboardButtonClick: HeaderMenuButtonProps['onClick'];
};
const DashboardButton: React.VFC<DashboardButtonProps> = (props) => {
return <HeaderMenuButton onClick={props.onDashboardButtonClick} label="Dashboard" />;
};
export default DashboardButton;
簡易的に実装したが、大体の役割は説明できていると思う。
続き
前
Discussion