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

# はじめに

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

https://qiita.com/MichaelHashimoto/items/f3ad9f56081a40b9b788

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

# Repositoryの選定

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

https://github.com/manuarora700/simple-developer-portfolio-website/tree/main

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1223968/27cab4fa-9412-4405-2907-eec30b247ec0.png)


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

# 編集内容

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1223968/442a0e9b-c9e1-14a9-ecce-efee07356e2e.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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1223968/ed7e9805-7a4c-1e92-4fe0-3ab74c77afd9.png)

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

Navbarコンポーネント
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1223968/37322af8-ed6c-4b0b-639e-a2ff62f74cc1.png)

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

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1223968/d7a55f6f-4f25-a7cb-3924-88f91855daba.png)

ContainerBlock.jsですね。

ContainerBlockコンポーネント
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1223968/76bcebe4-7387-5fe8-d9e7-7970ecf0180b.png)

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

同様に検索します。

たくさんヒットしました。あらゆるところで使われているコンポーネントみたいです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1223968/67d49011-a649-5f33-5e13-a7e61064d596.png)

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

Homeコンポーネントが書かれていました。
```jsx: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コンポーネントを見てみましょう。
```jsx: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です。

```jsx
<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のページに移動するなど)

以下の形で宣言されています。
```jsx
<Link href="移動先のリンク" className="指定したいクラス名">
リンクに表示したい文字列
</Link>
```
移動先のリンクにpagesディレクトリのファイルを指定すると、そのファイル内のコンポーネントが表示されるようになります。今回はhref="/experience"なので、遷移後にはpages/experience.js内のコンポーネントが表示されます。

#### useRouter

useRouterはNext.jsが提供しているフックの1種です。routerオブジェクトを返します。
```jsx
const router = useRouter();
```
このrouterオブジェクトは、現在表示中のページのパスに対する情報を持っています。
例えば、```router.asPath```を実行すれば、パスが取得できます。(http\://localhost:3000/experienceにアクセスすると、router.asPathで"/experience"が返ってくる)

Linkの中でclassNameを指定する際に、このrouterが使われています。
```jsx
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のページでのみ、矢印が表示されるようになっています。
```jsx
<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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1223968/de6dad67-ea26-e3bb-6ba7-4e50affa7a8d.png)

### 補足

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

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

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

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

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

```jsx
// 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>
  );
};
```

これを使うと、次のようなきれいなコードになります。
```jsx
<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の紹介](https://qiita.com/h-yoshikawa44/items/efa33101b0a02cba7759))

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

#### MUIのインストール・導入

```
// 依存関係を強行突破してインストール
npm install @mui/material @emotion/react @emotion/styled @mui/icons-material  --force
```

```jsx:
import Button from '@mui/material/Button';
import { Typography } from "@mui/material";
```

#### 編集

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

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


```jsx
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を使うコードはそのまま変更なしです。
```jsx
<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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1223968/91b3dd20-505a-3f93-7f7e-78d8e7777304.png)

#### さらに編集

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

NavLinkの引数に、iconを設定出来るようにします。
```jsx
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します。
```jsx
import {Info, History, Code, Email} from "@mui/icons-material";
```

NavLinkのpropsにiconを入れます。
```jsx
<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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1223968/0a615bfc-0a61-d94b-cca5-ed24ef251c19.png)


# 最後に

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

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

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

# 参考文献

https://qiita.com/TakanoriOkawa/items/21b49014c13c945bd194