まえがき
百聞は一見に如かずということで、典型的な管理画面を作りながらReactを学んでいく。
シリーズ形式の記事となります。今回は1つめ。
1. 【React】管理画面を作りながらReactを学ぶ(ChakraUI/コンポーネント化/react rooter v6)
2. 【React】管理画面を作りながらReactを学ぶ(props/ライフサイクル関数/API呼び出し)
3. 【React】管理画面を作りながらReactを学ぶ(ビルド)
この記事に書くこと
管理画面のレイアウト部分をメインに作りながら、下記について説明していく。
・ChakraUIの基本的な使い方(ヘッダ/サイドメニューの作成)
・Reactコンポーネントの分け方
・ルーティングのやり方(react rooter v6)
やっていこう
create-react-appプロジェクト作成
プロジェクト作成
・ChakraUIを使うため、templateとして@chakra-ui/typescript
を指定
yarn create react-app chakra-react-practice --template @chakra-ui/typescript
起動
yarn start
ChakraUIについておさらい
プロジェクト作成時点のApp.tsxの中身を見てみよう。
import * as React from "react"
import {
ChakraProvider,
Box,
Text,
Link,
VStack,
Code,
Grid,
theme,
} from "@chakra-ui/react"
import { ColorModeSwitcher } from "./ColorModeSwitcher"
import { Logo } from "./Logo"
export const App = () => (
<ChakraProvider theme={theme}>
<Box textAlign="center" fontSize="xl">
<Grid minH="100vh" p={3}>
<ColorModeSwitcher justifySelf="flex-end" />
<VStack spacing={8}>
<Logo h="40vmin" pointerEvents="none" />
<Text>
Edit <Code fontSize="xl">src/App.tsx</Code> and save to reload.
</Text>
<Link
color="teal.500"
href="https://chakra-ui.com"
fontSize="2xl"
target="_blank"
rel="noopener noreferrer"
>
Learn Chakra
</Link>
</VStack>
</Grid>
</Box>
</ChakraProvider>
)
ChakraProviderとは?
・ChakraUIが提供するコンポーネントを使うためには、ChakraProvider
を最も上位の階層で返す必要がある。
・propsとしてデフォルトのtheme
を渡している。カスタマイズしたい場合はここのthemeを変更する。
import {ChakraProvider, theme} from "@chakra-ui/react"
export const App = () => (
<ChakraProvider theme={theme}>
ヘッダ/サイドメニューを作る
・コンポーネントは分けず、App.tsx内だけでとりあえず書いてみた。
・サイドメニューとして使えそうなChakraUIコンポーネントとして「Drawer」があったが、これは名前の通りボタンをクリックしたら表示される(引き出される/drawされる)ものだったため、Boxコンポーネント、Buttonコンポーネントで実装。
import {
ChakraProvider,
Box,
Button,
Flex,
Icon,
Image,
theme,
} from "@chakra-ui/react";
import {
MdMessage,
MdDashboard,
MdEmail,
MdAccountBox,
MdInsertChart,
MdWbSunny,
MdRateReview,
} from "react-icons/md";
export const App = () => {
// SideMenuコンポーネント内に定義する
const sideMenuItems = [
{
name: "Dashboard",
icon: MdDashboard,
},
{
name: "Account",
icon: MdAccountBox,
},
{
name: "Mailbox",
icon: MdEmail,
},
{
name: "Message",
icon: MdMessage,
},
{
name: "Charts",
icon: MdInsertChart,
},
{
name: "Weather",
icon: MdWbSunny,
},
{
name: "RateReview",
icon: MdRateReview,
},
];
return (
<ChakraProvider theme={theme}>
<Flex w="100vw" h="100wh">
{/* Headerコンポーネントとして切り出す */}
<Flex
as="header"
position="fixed"
bg="gray.100"
top={0}
width="full"
height="100px"
shadow="sm"
py={4}
px={8}
>
<Image src="/sampleLogo.PNG" />
</Flex>
<Box mt="100px">
<Flex>
{/* SideMenuコンポーネントとして切り出す */}
<Box w="20vw" m="20px">
{sideMenuItems.map((item) => (
<label>
<Box mt="10px" ml="10px">
<Button variant="ghost">
<Icon as={item.icon} w={7} h={7} mr="13px" />
{item.name}
</Button>
</Box>
</label>
))}
</Box>
<Box w="70vw">
{/* サイドメニューアイコンをクリックする度にここが切り替わる */}
</Box>
</Flex>
</Box>
</Flex>
</ChakraProvider>
);
};
as propとは?
・デフォルトで全てのCharkraコンポーネントについている
・コンポーネントが出力するタグをオーバライド可能
・例) Flexコンポーネントはデフォルトでdiv要素として生成されるが、header要素として生成したい場合にas="header"
と指定する。
コンポーネント分ける
・ヘッダとサイドメニューをコンポーネント化する
・src/App.tsxに対して、コンポーネント化した「TopHeader.tsx」「SideMenu.tsx」はsrc/componentsフォルダに格納する。
src/
・App.tsx
・components/
・TopHeader.tsx
・SideMenu.tsx
src/App.tsx
import { ChakraProvider, Box, Flex, theme } from "@chakra-ui/react";
// componentsフォルダ配下のTopHeader.tsx、SideMenu.tsxをインポート
import { TopHeader } from "./components/TopHeader";
import { SideMenu } from "./components/SideMenu";
export const App = () => {
return (
<ChakraProvider theme={theme}>
<Flex w="100vw" h="100wh">
<TopHeader />
<Box mt="100px">
<Flex>
<SideMenu />
<Box w="70vw">
{/* サイドメニューアイコンをクリックするとここが切り替わる */}
</Box>
</Flex>
</Box>
</Flex>
</ChakraProvider>
);
};
src/components/TopHeader.tsx
import { Flex, Image } from "@chakra-ui/react";
export const TopHeader = () => (
<Flex
as="header"
position="fixed"
bg="gray.100"
top={0}
width="full"
height="100px"
shadow="sm"
py={4}
px={8}
>
<Image src="/sampleLogo.PNG" />
</Flex>
);
src/components/SideMenu.tsx
import { Box, Button, Icon } from "@chakra-ui/react";
import {
MdMessage,
MdDashboard,
MdEmail,
MdAccountBox,
MdInsertChart,
MdWbSunny,
MdRateReview,
} from "react-icons/md";
export const SideMenu = () => {
const sideMenuItems = [
{
name: "Dashboard",
icon: MdDashboard,
},
{
name: "Account",
icon: MdAccountBox,
},
{
name: "Mailbox",
icon: MdEmail,
},
{
name: "Message",
icon: MdMessage,
},
{
name: "Charts",
icon: MdInsertChart,
},
{
name: "Weather",
icon: MdWbSunny,
},
{
name: "RateReview",
icon: MdRateReview,
},
];
return (
<Box w="20vw" m="20px">
{sideMenuItems.map((item) => (
<label>
<Box mt="10px" ml="10px">
<Button variant="ghost">
<Icon as={item.icon} w={7} h={7} mr="13px" />
{item.name}
</Button>
</Box>
</label>
))}
</Box>
);
};
サイドメニューアイコンをクリックしたら画面が切り替わるようにする
サイドメニューアイコンををクリックしたら、共通部分(ヘッダ/サイドメニュー)は変わることなく画面(Dashboard,Account,etc)だけ切り替わるようにしたい。これをReactで実現するにはReact Rooterを利用する必要がある。
React Rooterをインストール
yarn add react-router-dom
・今(2022/03/05)のReact Rooterのバージョンは「6」
・バージョンごとに書き方が異なるため、ぐぐると色々でてくるのでバージョンを意識しておく。
ルーティング先の画面を用意
ルーティング設定の前に、ルーティング先の画面をroutesフォルダ配下に用意する。
src/
・App.tsx
・routes/
・Dashboard.tsx
・Account.tsx
・MailBox.tsx
・Message.tsx
・Charts.tsx
・Weather.tsx
・RateReview.tsx
src/routes/Dashboard.ts
・他の画面も同じ要領で作成する。
export const Dashboard = () => <h1>Dashboard</h1>;
ルーティング設定
ルーティングで切り替えたいのは{/* サイドメニューアイコンをクリックするとここが切り替わる */}
の部分である。
App.tsx
export const App = () => {
return (
<ChakraProvider theme={theme}>
<Flex w="100vw" h="100wh">
<TopHeader />
<Box mt="100px">
<Flex>
<SideMenu />
<Box w="70vw">
{/* サイドメニューアイコンをクリックするとここが切り替わる */}
</Box>
</Flex>
</Box>
</Flex>
</ChakraProvider>
);
};
↓ルーティング設定を追加
import { ChakraProvider, Box, Flex, theme } from "@chakra-ui/react";
import { TopHeader } from "./components/TopHeader";
import { SideMenu } from "./components/SideMenu";
// ルーティング設定に必要なものをimport
import { BrowserRouter, Routes, Route } from "react-router-dom";
// ルーティング先の画面コンポーネントをimport
import { Account } from "./routes/Account";
import { Charts } from "./routes/Charts";
import { Dashboard } from "./routes/Dashboard";
import { MailBox } from "./routes/MailBox";
import { Message } from "./routes/Message";
import { RateReview } from "./routes/RateReview";
import { Weather } from "./routes/Weather";
export const App = () => {
return (
<ChakraProvider theme={theme}>
<Flex w="100vw" h="100wh">
<TopHeader />
<Box mt="100px">
<Flex>
// navigate()はこのBrowserRouter内で呼び出す必要がある。
<BrowserRouter>
<SideMenu />
<Box w="70vw">
<Routes>
<Route path="/" element={<Dashboard />} />
<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>
);
};
アイコンクリックしたらルーティングするように修正
src/components/SideMenu.tsx
// react rooter v6で画面遷移するために使うやつ
import { useNavigate } from "react-router-dom";
export const SideMenu = () => {
// natigate関数を取得
const navigate = useNavigate();
return (
<Box w="20vw" m="20px">
{sideMenuItems.map((item) => (
<label>
<Box mt="10px" ml="10px">
// ボタンをクリックしたらルーティングする
<Button variant="ghost" onClick={() => navigate(item.path)}>
<Icon as={item.icon} w={7} h={7} mr="13px" />
{item.name}
</Button>
</Box>
</label>
))}
</Box>
);
};
ルーティング出来ることの確認
http://localhost:3000/ にアクセス
↓「Account」アイコンをクリック
・URLが http://localhost:3000/account に遷移している
ChakraUIメモ
Box
・ただのdiv要素。ChakraUIの色んなStyleを適用できる便利なdiv要素、という認識。
・Box is the most abstract component on top of which all other Chakra UI components are built. By default, it renders a div element.
Flex
・「display: flex」のBoxコンポーネント
・Flex is Box with display: flex and comes with helpful style shorthand. It renders a div element.
Icon
・Chakra UI iconsだと物足りない感があったのでreact-icons
を利用する
react-icons一覧
↓のコードだと「歯車アイコン(MdSettings)」が表示される。
// 1. Import
import { Icon } from '@chakra-ui/react'
import { MdSettings } from 'react-icons/md'
// 2. Use the `as` prop
function Example() {
return <Icon as={MdSettings} />
}
Button
・↑のアイコンと組み合わせる
<Button colorScheme="gray"
leftIcon={<Icon as={MdAccountBox} w={9} h={9} />}
variant="ghost"
>
<Text m="10px">アカウント</Text>
</Button>
・colorSchemeに指定可能な色一覧
・variantで枠のデザインを指定