0
0

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.

コード譜投稿アプリを作る #2(chakra-uiインストールからレイアウトコンポーネント実装まで)

Last updated at Posted at 2022-08-24

←前回の記事

chakra-uiのインストール

chakraの公式サイトにあるコマンドを叩く

% yarn add @chakra-ui/react @emotion/react @emotion/styled framer-motion

後々iconsも使うことになったので、これもインストールしときます

% yarn add @chakra-ui/icons

なんかコンパイルエラー出た

Could not find a declaration file for module ‘lodash’というエラーが出た。
lodashの型が必要らしい

以下のコマンドでlodashの型をインストールします。

% yarn add @types/lodash

tsconfig.json内のconpilerOptions.typesを以下のように変更。

tsconfig.json
{
  "compilerOptions": {
    ...
-   "incremental": true
+   "incremental": true,
+   "types" : ["node", "lodash"]
  },
}

chakra使えてるか確認

chakraを使うには使いたい要素をChakraProviderタグで囲む必要がある。
_app.tsxを以下のように編集。

/pages/_app.tsx
import type { AppProps } from 'next/app'
import { ChakraProvider } from '@chakra-ui/react'

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <ChakraProvider>
      <Component {...pageProps} />
    </ChakraProvider>  
  )
}

export default MyApp

これでchakra使えるはずなので、トップページを編集します。

/pages/index.tsx
import { Text } from '@chakra-ui/react'
import type { NextPage } from 'next'


const Home: NextPage = () => {
  return (
    <Text color='red'>hello world</Text>
  )
}

export default Home

image.png
いけてそう!

レイアウトコンポーネントで、レイアウトを共通化する

ヘッダーやフッターなどを各ページで共通化したいので、Next.jsのドキュメントに従ってやっていきます。
ヘッダーはサンプルを参考にして以下のように仮作成しました。(Footerはとりあえず空の要素を返すだけにしてます)

/components/common/Layout/Header/index.tsx
import React from "react";
import {
  Box,
  Stack,
  Heading,
  Flex,
  Text,
  Button,
  useDisclosure
} from "@chakra-ui/react";
import { HamburgerIcon } from "@chakra-ui/icons";

export const Header = () => {
  const { isOpen, onOpen, onClose } = useDisclosure();
  const handleToggle = () => (isOpen ? onClose() : onOpen());

  return (
    <Flex
      as="nav"
      align="center"
      justify="space-between"
      wrap="wrap"
      padding={6}
      bg="teal.500"
      color="white"
    >
      <Flex align="center" mr={5}>
        <Heading as="h1" size="lg" letterSpacing={"tighter"}>
          Chakra UI
        </Heading>
      </Flex>

      <Box display={{ base: "block", md: "none" }} onClick={handleToggle}>
        <HamburgerIcon />
      </Box>

      <Stack
        direction={{ base: "column", md: "row" }}
        display={{ base: isOpen ? "block" : "none", md: "flex" }}
        width={{ base: "full", md: "auto" }}
        alignItems="center"
        flexGrow={1}
        mt={{ base: 4, md: 0 }}
      >
        <Text>Docs</Text>
        <Text>Examples</Text>
        <Text>Blog</Text>
      </Stack>

      <Box
        display={{ base: isOpen ? "block" : "none", md: "block" }}
        mt={{ base: 4, md: 0 }}
      >
        <Button
          variant="outline"
          _hover={{ bg: "teal.700", borderColor: "teal.700" }}
        >
          Create account
        </Button>
      </Box>
    </Flex>
  );
};

共通化したいレイアウトは以下のようなものです。

/component/common/Layout/index.tsx
import { Footer } from './Footer'
import { FC, ReactNode } from 'react'
import { Header } from './Header'

type Props = {
  children: ReactNode
}

export const Layout: FC<Props> = ({ children }) => {
  return (
    <>
      <Header />
        <main>{children}</main>
      <Footer />
    </>
  )
}

このLayoutコンポーネントでメインとなる要素を囲めば、ヘッダーとフッターが表示されるという寸法です。
レイアウトの実装には2通りあり、全ページ共通のレイアウトを_app.tsxで設定するやり方と、各ページのindex.tsxからgetLayoutメソッドを呼んでレイアウトを使うか使わないか、もしくはどのレイアウトを使うのかを指定するやり方があるそうです。
ゆくゆくはadminページも作れればいいなと思っているので、今回はadmin用ページとユーザー用ページでレイアウトを出し分けられるように後者のやり方を参考にします。

app.tsxを以下のように編集。

/pages/_app.tsx
import type { ReactElement, ReactNode } from 'react'
import type { NextPage } from 'next'
import type { AppProps } from 'next/app'
import { ChakraProvider } from '@chakra-ui/react'

export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
  getLayout?: (page: ReactElement) => ReactNode
}

type AppPropsWithLayout = AppProps & {
  Component: NextPageWithLayout
}

export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
  const getLayout = Component.getLayout ?? ((page) => page)

  return (
    <ChakraProvider>
      {getLayout(<Component {...pageProps} />)}
    </ChakraProvider>
  )
}

これで例えばトップページで先ほど作ったLayoutコンポーネントを使用したい場合は以下のようにします。

/pages/index.tsx
import { Box } from '@chakra-ui/react'
import type { ReactElement } from 'react'
import { Layout } from '../components/common/Layout'
import type { NextPageWithLayout } from './_app'

const Page: NextPageWithLayout = () => {
  return <Box>hello world</Box>
}

Page.getLayout = function getLayout(page: ReactElement) {
  return (
    <Layout>
      {page}
    </Layout>
  )
}

export default Page

レイアウトを使用せず、メインコンテンツをそのまま表示したい場合はreturn内を

return page

のみにし、
別のレイアウトを使いたい場合は

return (
  <AnotherLayout>
    {page}
  </AnotherLayout>
)

のようにすればよさそうです。
またこのNext.jsのレイアウト機能を使うと、ページを遷移してもレイアウト部分のstateを保持してくれるらしいです。

image.png

おkおk。

参考にしたサイト

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?