6
8

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】管理画面を作りながらReactを学ぶ(ChakraUI/コンポーネント化/react rooter v6)

Last updated at Posted at 2022-03-05

まえがき

百聞は一見に如かずということで、典型的な管理画面を作りながらReactを学んでいく。

シリーズ形式の記事となります。今回は1つめ。
1. 【React】管理画面を作りながらReactを学ぶ(ChakraUI/コンポーネント化/react rooter v6)
2. 【React】管理画面を作りながらReactを学ぶ(props/ライフサイクル関数/API呼び出し)
3. 【React】管理画面を作りながらReactを学ぶ(ビルド)

この記事に書くこと

管理画面のレイアウト部分をメインに作りながら、下記について説明していく。
・ChakraUIの基本的な使い方(ヘッダ/サイドメニューの作成)
・Reactコンポーネントの分け方
・ルーティングのやり方(react rooter v6)

最後に完成する管理画面
image.png

やっていこう

create-react-appプロジェクト作成

プロジェクト作成

・ChakraUIを使うため、templateとして@chakra-ui/typescriptを指定

yarn create react-app chakra-react-practice --template @chakra-ui/typescript

起動

yarn start

初期画面こんな感じ。
image.png

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

ヘッダ/サイドメニューを作る

image.png

・コンポーネントは分けず、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>
  );
};

サイドメニューアイコンをクリックしたら画面が切り替わるようにする

image.png
サイドメニューアイコンををクリックしたら、共通部分(ヘッダ/サイドメニュー)は変わることなく画面(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/ にアクセス
image.png
↓「Account」アイコンをクリック
image.png
・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>

image.png

・colorSchemeに指定可能な色一覧

image.png

・variantで枠のデザインを指定

image.png

6
8
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
6
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?