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?

実践編 GitHubの既存のRepositoryの編集 [5日間でReactの基礎を習得したい 4/5日目]

Last updated at Posted at 2024-05-05

はじめに

5日間でReactの基礎を習得する試みの4日目です。4日目は、GitHubの既存のRepositoryを編集することで、実践的に学びます。

私はC#を仕事で使っているため、オブジェクト指向の言語的な考え方を使って理解を進めます。同じような境遇の方の理解の助けになれば幸いです。

Repositoryの選定

Manu Aroraさんが作られたこちらのReactのポートフォリオを編集します。

image.png

選定理由は以下です。

  • starが300を超えていること
  • next.jsで作られていること
  • TypeScriptが使われていないこと(今回の勉強の対象外であるため)
  • 見た目がきれいであること
  • 自由に使って良いという記載があったこと

編集内容

今回は、ナビゲーションバーを編集することで、理解を深めようと思います。

image.png
各文字はリンクになっていて、クリックするとページが遷移します。

準備

GitHubからリポジトリをクローンします。

git clone https://github.com/manuarora700/simple-developer-portfolio-website.git

ディレクトリに移動し、必要なパッケージをインストールします。

cd simple-developer-portfolio-website
npm install --force

通常はnpm installでインストールするべきですが、依存関係でエラーが発生したため、今回は無理やりnpm install --forceで突破しました。恐らく良くないですが、時間の関係で解決は諦めました。

起動しましょう。

npm run dev

コードリーディング

ナビゲーションバーのコンポーネントを見つける

ナビゲーションバーのリンクには、"About","Projects","Experience","Contact"の4つがあります。まず、"Experience"という文字が使われているところを、全文検索で探しました。

image.png

いくつかファイルがヒットしましたが、リンクなので、href=となっているはずです。
つまり、Navbar.jsにナビゲーションバーのコンポーネントがありました。

Navbarコンポーネント
image.png

Navbarコンポーネントを呼び出しているコンポーネント

Navbarを使っているコンポーネントを探しましょう。全文検索です。

image.png

ContainerBlock.jsですね。

ContainerBlockコンポーネント
image.png

ContainerBlockコンポーネントを呼び出しているコンポーネント

同様に検索します。

たくさんヒットしました。あらゆるところで使われているコンポーネントみたいです。
image.png

この中には、index.jsがあります。Next.jsでは、page/index.jsのコンポーネントが最初に表示されるページ(エントリーポイント)として描画されるため、ここを見てみます。

Homeコンポーネントが書かれていました。

index.js
export default function Home({ repositories }) {
  return (
    <ContainerBlock
      title="Manu Arora - Developer, Writer, Creator"
      description="This is a template built specifically for my blog - Creating a developer portfolio that gets you a job."
    >
      <Hero />
      <FavouriteProjects />
      <LatestCode repositories={repositories} />
    </ContainerBlock>
  );
}

つまり、エントリーポイントにアクセスする
=> Homeコンポーネントが描画される
=> その中のContainerBlockが描画される
=> その中のNavbarコンポーネントが描画される
という訳です。

ContainerBlockが色々なページから呼び出されているのは、全ページのヘッダーとフッター部分を描画するコンポーネントだからでしょう。ナビゲーションバーはヘッダーの一部ですね。

Navbarコンポーネント

では、Navbarコンポーネントを見てみましょう。

Navbar.js
import React, { useEffect, useState } from "react";
import Link from "next/link";
import { useTheme } from "next-themes";
import { useRouter } from "next/router";
import userData from "@constants/data";

export default function Navbar() {
  const router = useRouter();
  console.log(router.asPath);
  const { theme, setTheme } = useTheme();
  const [mounted, setMounted] = useState(false);

  useEffect(() => {
    setMounted(true);
  }, []);

  return (
    <div className="max-w-6xl  mx-auto px-4 py-10 md:py-20">
      <div className="flex  md:flex-row justify-between items-center">
        <div className="flex flex-col">
          <Link href="/">
            <h1 className="font-semibold text-xl dark:text-gray-100">
              {userData.name}
            </h1>
            <p className="text-base font-light text-gray-500 dark:text-gray-300">
              {userData.designation}
            </p>
          </Link>
        </div>

        <div className="space-x-8 hidden md:block">
          <Link
            href="/experience"
            className={`text-base  ${
              router.asPath === "/experience"
                ? "text-gray-800 font-bold dark:text-gray-400"
                : "text-gray-600 dark:text-gray-300 font-normal "
            }`}
          >
            Experience{" "}
            {router.asPath === "/experience" && (
              <svg
                xmlns="http://www.w3.org/2000/svg"
                width="16"
                height="16"
                fill="currentColor"
                className="bi bi-arrow-down inline-block h-3 w-3"
                viewBox="0 0 16 16"
              >
                <path
                  fillRule="evenodd"
                  d="M8 1a.5.5 0 0 1 .5.5v11.793l3.146-3.147a.5.5 0 0 1 .708.708l-4 4a.5.5 0 0 1-.708 0l-4-4a.5.5 0 0 1 .708-.708L7.5 13.293V1.5A.5.5 0 0 1 8 1z"
                />
              </svg>
            )}
          </Link>

          // その他のリンクもおおよそ同じ実装なので省略。
        </div>

        <div className="space-x-4 flex flex-row items-center">
          // 省略。SNSへのリンクを表示するコード。
        </div>
      </div>
      <div className="space-x-8 block md:hidden mt-4">
        // 省略。モバイルサイズのときに、サイズを調整してリンクを表示するコード。
      </div>
    </div>
  );
}

ナビゲーションバーを編集する上で、理解すべきなのはLinkコンポーネントとuseRouterです。

<Link
    href="/experience"
    className={`text-base  ${
      router.asPath === "/experience"
        ? "text-gray-800 font-bold dark:text-gray-400"
        : "text-gray-600 dark:text-gray-300 font-normal "
    }`}
    >
    Experience{" "}
    
    // 一旦省略するが、ここにページによって表示の有無を切り替えたい要素の記載あり
</Link>

Linkコンポーネント

Linkコンポーネントは、Next.jsが提供しているコンポーネントです。これを使えば、該当要素をクリックすることで、ページ内の遷移が可能です。(HomeからExperienceのページに移動するなど)

以下の形で宣言されています。

<Link href="移動先のリンク" className="指定したいクラス名">
リンクに表示したい文字列
</Link>

移動先のリンクにpagesディレクトリのファイルを指定すると、そのファイル内のコンポーネントが表示されるようになります。今回はhref="/experience"なので、遷移後にはpages/experience.js内のコンポーネントが表示されます。

useRouter

useRouterはNext.jsが提供しているフックの1種です。routerオブジェクトを返します。

const router = useRouter();

このrouterオブジェクトは、現在表示中のページのパスに対する情報を持っています。
例えば、router.asPathを実行すれば、パスが取得できます。(http://localhost:3000/experienceにアクセスすると、router.asPathで"/experience"が返ってくる)

Linkの中でclassNameを指定する際に、このrouterが使われています。

className={`text-base  ${
  router.asPath === "/experience"
    ? "text-gray-800 font-bold dark:text-gray-400"  // /experience表示時に付与されるクラス名
    : "text-gray-600 dark:text-gray-300 font-normal "  // それ以外の表示時に付与されるクラス名
}`}

NavBarはあらゆるところから呼ばれており、experience.jsからも呼ばれています。そのため、上記のコードではexperience.jsにアクセスすると、条件分岐によってclassNameが切り替わるようになっているようです。

同じ原理を使って、このLinkコンポーネント内では、/experienceのページでのみ、矢印が表示されるようになっています。

<Link
href="/experience"
className={`text-base  ${
  router.asPath === "/experience"
    ? "text-gray-800 font-bold dark:text-gray-400"
    : "text-gray-600 dark:text-gray-300 font-normal "
}`}
>
Experience{" "}

// これ以降がページによって表示の有無を切り替えたい要素
// /experienceのページでは、以下の要素(svgで書かれる矢印)が表示される。
{router.asPath === "/experience" && (
  <svg
    xmlns="http://www.w3.org/2000/svg"
    width="16"
    height="16"
    fill="currentColor"
    className="bi bi-arrow-down inline-block h-3 w-3"
    viewBox="0 0 16 16"
  >
    <path
      fillRule="evenodd"
      d="M8 1a.5.5 0 0 1 .5.5v11.793l3.146-3.147a.5.5 0 0 1 .708.708l-4 4a.5.5 0 0 1-.708 0l-4-4a.5.5 0 0 1 .708-.708L7.5 13.293V1.5A.5.5 0 0 1 8 1z"
    />
  </svg>
)}
</Link>

/experienceのページにアクセスすると、"Experience"の文字の右に矢印が表示されます。
image.png

補足

classNameに色々と指定されていますが、これはTailwindCSSを使っているようです。例えば、className="flex"が指定されることで、すべての要素が横並びに配置されています。
ただし、今回の範囲外なので立ち入りません。

ナビゲーションバーを編集する

それでは、ナビゲーションバーを編集します。

その前にリファクタリング

ナビゲーションバーのLinkには、"About","Projects","Experience","Contact"の4つがあります。もともとの実装では、それぞれ同じようなコードが重複して書かれています。これだと、毎回4つともコピペで編集しないといけないので、リファクタリングします。
Link間で異なるのは、hrefの値とLink間に記載するtextでした。そのため、これらを引数にし、共通部分をすべてまとめたNavLinkコンポーネントを作り、Navbar.jsに追記しました。

// chatGPTに任せました
export const NavLink = ({ href, children }) => {
  const router = useRouter();
  const isActive = router.asPath === href;

  const className = isActive
    ? "text-gray-800 font-bold dark:text-gray-400"
    : "text-gray-600 dark:text-gray-300 font-normal";

  return (
    <Link href={href} className={`text-base ${className}`}>
      {children}
      {isActive && (
        <svg
          xmlns="http://www.w3.org/2000/svg"
          width="16"
          height="16"
          fill="currentColor"
          className="bi bi-arrow-down inline-block h-3 w-3"
          viewBox="0 0 16 16"
        >
          <path fillRule="evenodd" d="M8 1a.5.5 0 0 1 .5.5v11.793l3.146-3.147a.5.5 0 0 1 .708.708l-4 4a.5.5 0 0 1-.708 0l-4-4a.5.5 0 0 1 .708-.708L7.5 13.293V1.5A.5.5 0 0 1 8 1z" />
        </svg>
      )}
    </Link>
  );
};

これを使うと、次のようなきれいなコードになります。

<div className="space-x-8 hidden md:block">
    <NavLink href="/about">About</NavLink>
    <NavLink href="/projects">Projects</NavLink>
    <NavLink href="/experience">Experience</NavLink>
    <NavLink href="/contact">Contact</NavLink>
</div>

Reactにおいて、コンポーネントの間に挟んだコンポーネントと文字列はchildプロパティとなり、自動的に引数のchildに格納されます。今回の場合は<NavLink href="**">text</NavLink>のtextがchildに入ります。

編集してみる

ChatGPTにNavbar.jsをコピペし、編集する課題を出してもらいました。ただし、条件として"Material UI"を使った課題であること"を要求しました。世の中でよく使われており、使ってみたかったからです。(参考: Material UIの紹介)

課題内容

MUIのButtonコンポーネントとTypographyコンポーネントを使って、NavLinkコンポーネントをスタイリッシュに再設計する。

MUIのインストール・導入

// 依存関係を強行突破してインストール
npm install @mui/material @emotion/react @emotion/styled @mui/icons-material  --force
import Button from '@mui/material/Button';
import { Typography } from "@mui/material";

編集

Material UIの公式サイトで、ButtonとTypographyのサンプルコードを見て、使ってみました。

Typographyコンポーネントは、childに含まれる文字列が読みやすくなるように登録された、文字サイズやフォントなどのプリセットを指定できるコンポーネントです。

export const NavLink = ({ href, children}) => {
  const router = useRouter();
  const isActive = router.asPath === href;

  const className = isActive
    ? "text-gray-800 font-bold dark:text-gray-400"
    : "text-gray-600 dark:text-gray-300 font-normal";

  return (
    <Link href={href} className={`text-base ${className}`}>
      // ここからMUIを追加した部分
      <Button color="secondary">  // colorに"secondary"というプリセットを指定
        <Typography variant="body1">  // body1というプリセットを指定
          {children}
        </Typography>
      </Button>
      // ここまで
      {isActive && (
        <svg
          xmlns="http://www.w3.org/2000/svg"
          width="16"
          height="16"
          fill="currentColor"
          className="bi bi-arrow-down inline-block h-3 w-3"
          viewBox="0 0 16 16"
        >
          <path fillRule="evenodd" d="M8 1a.5.5 0 0 1 .5.5v11.793l3.146-3.147a.5.5 0 0 1 .708.708l-4 4a.5.5 0 0 1-.708 0l-4-4a.5.5 0 0 1 .708-.708L7.5 13.293V1.5A.5.5 0 0 1 8 1z" />
        </svg>
      )}
    </Link>
  );
};

NavLinkを使うコードはそのまま変更なしです。

<div className="space-x-8 hidden md:block">
  <NavLink href="/about">About</NavLink>
  <NavLink href="/projects">Projects</NavLink>
  <NavLink href="/experience">Experience</NavLink>
  <NavLink href="/contact">Contact</NavLink>
</div>

これで完成です。うまく変更できました。
image.png

さらに編集

折角なので、ボタンの左側にiconを入れることにしました。

NavLinkの引数に、iconを設定出来るようにします。

export const NavLink = ({ href, children, icon }) => {
  // 省略

  return (
    <Link href={href} className={`text-base ${className}`}>
      <Button color="secondary">
        {icon}
        <Typography variant="body1">
          {children}
        </Typography>
      </Button>
      
      // 省略
      
    </Link>
  );
};

公式サイトのIconのカタログから使いたいIconを選び、importします。

import {Info, History, Code, Email} from "@mui/icons-material";

NavLinkのpropsにiconを入れます。

<div className="space-x-8 hidden md:block">
  <NavLink href="/about" icon={<Info/>}>About</NavLink>
  <NavLink href="/projects" icon={<Code/>}>Projects</NavLink>
  <NavLink href="/experience" icon={<History/>}>Experience</NavLink>
  <NavLink href="/contact" icon={<Email/>}>Contact</NavLink>
</div>

完成です!
image.png

最後に

今まで3日間勉強してきた内容を踏まえれば、色々調べながらではあるものの、コードリーディング/実装を進められることが分かりました。
また、Material UIでは、公式サイトのドキュメントが充実しているため、引数に関する情報をよく読めば、どんどん使える予感がしました。

次回は最終回です。ここで修正したコードのテストを作ります。

何か間違いがあれば、是非コメントを頂けると助かります!

参考文献

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?