Gatsby組込みのLinkコンポーネントを使ってナビゲーションバーを自作する

# 初めに

この記事は[Gatsby.js Advent Calendar](https://qiita.com/advent-calendar/2019) 2019 21日目の記事です。
GatsbyJSを色々いじってたらいい感じのナビゲーションバーが自作できた気がしました。

# 目次
 
 * 本記事の目標
 * 前提 - 開発環境 -
 * Gatsby Link APIについて
 * ディレクトリ構成
 * まずはひな型を作る
 * 共通のレイアウトは`Layout`コンポーネントとして定義する
 * `Header`コンポーネントの実装
 * 終わりに

# 本記事の目標

こんな感じのナビゲーションバーを作りたいと思います。
それっぽいものがフロントエンド初心者の私でも作れたので投下します。
重要なことですが、このレイアウトでは**Bootstrap4は使っていません。**

サンプルを公開しています。触ってみてください → [サンプル](https://agitated-curran-0281f4.netlify.com/)
ソースコード→ https://github.com/koralle/gatsby-link-navbar

![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/226452/a299d119-a27a-c2b8-5df7-6e96c3085e48.png)



# 前提 - 開発環境 - 

以下私の手元の開発環境になります。

* Windows 10
* yarn 1.21.1
* Gatsby CLI version: 2.8.15

# Gatsby Link APIについて

このナビゲーションバーを作るうえで、今回は**Gatsby組込みのGatsby Link API**を使用しています。
簡単に説明すると、サイト内リンクをこのGatsby Link APIを使って実装すると、そのリンクを踏んで画面遷移する前に、関連するリソースを事前にfetchしてくれる（公式ではこれを"**preloading**"と呼んでいる）ので、結果的にサイトパフォーマンスの向上につながります。

今回はこのGatsby Link APIの中の**`Link`**コンポーネントを使用します。

公式ドキュメント→[Gatsby Link API | GatsbyJS](https://www.gatsbyjs.org/docs/gatsby-link/)


これに関しては12/25の投稿日に記事を書けたらいいなと思います。


# ディレクトリ構成

`gatsby-link-navbar`というプロジェクトを作成し、`/src`以下のディレクトリを編集しました。
`gatsby-link-navbar/src/components/`以下のファイルが画面のレイアウトを作ります。


```console
> tree src
HOGEHOGE\GATSBY-LINK-NAVBAR\SRC
├─components
│  ├─Header
│  │      Header.css
│  │      Header.js
│  │
│  └─Layout
│          Layout.css
│          Layout.js
│
└─pages
        about.js
        hobby.js
        index.js
        link.js
        skills.js
```


このような構成にすることで、リソース、URL、そしてコンテンツが以下のような対応関係になるようにしました。
ただ、今回は意図したレイアウトが描画されていることが確認できればOKなので、`gatsby-link-navbar/src/pages/`以下は今回ほとんど作りこんでません。

リソース              |  URL                     |コンテンツ
----------------------+-------------------------+-----------
./src/pages/about.js  |  localhost:8000/about/ | "About"
./src/pages/hobby.js  |  localhost:8000/hobby/ | "Hobby"
./src/pages/index.js  |  localhost:8000        | トップ画面
./src/pages/link.js   |  localhost:8000/link/  | "Link"
./src/pages/skills.js |  localhost:8000/skills/| "Skills"




# まずはひな型を作る

今回はstarterとして`gatsby-starter-hello-world`を使います。

```
> gatsby new gatsby-link-navbar https://github.com/gatsbyjs/gatsby-starter-hello-world
```

次のコマンドを叩いた後、ブラウザで`localhost:8000`にアクセスして"Hello, world!"が表示されていることを確認してください。

```
> cd gatsby-link-navbar
> gatsby develop
```

# 共通のレイアウトは`Layout`コンポーネントとして定義する

まずは全ページ共通のレイアウトを`Layout`コンポーネント(**`layout.js`**)として作成します。
先に`layout.js`の内部を確認します。

```jsx:layout.js
import React from 'react';
import Header from '../Header/Header';
import './Layout.css';

export default ({ children }) => (
    <div className="page-root">
        <Header />
        <div className="page-body">
            {children}
        </div>
    </div>
)
```

大雑把に言えば`Header`コンポーネントとページの中身を収容する`<div>`タグを縦に並べています。
`Layout`コンポーネントをこのように定義すると、個々のコンテンツを描画するコードは以下のように書くことができます。

```jsx:./src/pages/hogehoge.js

{/* "hogehoge"ページ */}
const Hogehoge = () => {
    return (
        <Layout>
　　　　　　{/* ここにlayout.js内の{children}に当たる内容を記述する。 */}
        </Layout>
    );
}
```

`Layout.js`は`Header.js`に依存しているので、次に`Header`コンポーネントつまりナビゲーションバーを実装します。

# `Header`コンポーネントの実装

`Link`コンポーネントを使用するために、まずはLink APIをインポートします。

```jsx
// Gatsby標準組込みなので事前の"yarn add"や"npm install"の必要はなし。
import { Link } from 'gatsby';
```

私はナビゲーションバーに表示したい項目を例えば以下のようにしました。

```jsx
// ナビゲーションバーに表示する項目
const NavMenuItem = ["Home", "About", "Skills", "Hobby", "Link"];
```

次に、ナビゲーションバーの項目のそれぞれに設定するスタイルを記述します。

今回はアクティブになっている項目の`Link`コンポーネントは色を反転させ、太字にします。
私は`Header.js`内ではアクティブ時と非アクティブ時の差分のみを記述し、それ以外でLinkコンポーネントに適用したいスタイルは同じ階層の`Header.css`に記述しました（この辺は好みの問題だと思います）。

```jsx

// 普段のリンクはこのスタイル
const LinkStyles = {
    background: 'rebeccapurple',
    color: 'white',
    fontWeight: "normal"
}

// アクティブになった項目は色を反転させる
const ActiveStyles = {
    background: 'white',
    color: 'rebeccapurple',
    fontWeight: "bold",
}
```

この二つのスタイルをナビゲーションバーに表示する項目（=`Link`コンポーネント）**全て**に適用します。
**アクティブ時と非アクティブ時の切り替えはGatsby側が引き受けてくれるので、スタイルの切り替え処理を私たちが記述する必要がありません。**

```jsx
const NavMenuLiTag = NavMenuItem.map((item) => {
    let page_link = "";
    if (item === "Home") {
        page_link = "/";
    }
    // e.g.) "/about/", "/hobby/"
    else page_link = "/" + item.toLowerCase() + "/"; 

    return (
        <li key={page_link}>
            <Link to={page_link} style={LinkStyles}
                activeStyle={ActiveStyles}
                className="page-link"
            >
                {item}
            </Link>
        </li>
    )
});
```


最終的に`Header`コンポーネントは以下のようになりました。

```jsx:./src/components/Header/Header.js
import React from 'react';
import { Link } from 'gatsby';
import './Header.css'

const Header = (props) => {
    // ナビゲーションバーに表示するリンク
    const NavMenuItem = ["Home", "About", "Skills", "Hobby", "Link"];

    // 普段のリンクはこのスタイル
    const LinkStyles = {
        background: 'rebeccapurple',
        color: 'white',
        fontWeight: "normal"
    }

    // アクティブになったリンクは色を反転させる
    const ActiveStyles = {
        background: 'white',
        color: 'rebeccapurple',
        fontWeight: "bold",
    }

    // ナビゲーションリンクの作成
    const NavMenuLiTag = NavMenuItem.map((item) => {
        let page_link = "";
        if (item === "Home") {
            page_link = "/";
        }
        else page_link = "/" + item.toLowerCase() + "/";

        return (
            <li key={page_link}>
                <Link to={page_link} style={LinkStyles}
                    activeStyle={ActiveStyles}
                    className="page-link"
                >
                    {item}
                </Link>
            </li>
        )
    });

    return (
        <header className="App-header">
            <nav className="App-navbar">
                <p className="App-logo"><Link to="/" >koralle</Link></p>
                <div className="App-navbar-item">
                    <ul>
                        {NavMenuLiTag}
                    </ul>
                </div>
            </nav>
        </header>
    );
}

export default Header;
```


# 終わりに

訂正等あればコメントお願いします...