React + Netlify + microCMSを使ってイケてるポートフォリオを作った

# 本記事でやること
Reactを使ったフロントエンド開発を、今時のサービスを使って簡単に効率よくできる方法を紹介します。
また、ポートフォリオの設計に関してや実装から得られたナレッジ、知っておいたほうがいい実装方法を紹介します。
私のポートフォリオはNetlifyのサーバーに上げているので、実際の挙動などは https://ykonishi.tokyo から確認できます。

ちなみにポートフォリオのソースコードはGitHubにも上げているので、こちらも見ていただけたらと思います。
[Yuichi Konishiポートフォリオサイトのソースコード](https://github.com/nisyuu/portfolio-ykonishi)

# Reactとは
最近はよくSPA（Single Page Application）という言葉をよく耳にしますが、そのSPAの一つがReactです。
特徴としては、[データバインディング](https://qiita.com/nisyuu/items/61268b1e308123ecd98c#%E3%83%87%E3%83%BC%E3%82%BF%E3%83%90%E3%82%A4%E3%83%B3%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0)、[仮想DOM](https://qiita.com/nisyuu/items/61268b1e308123ecd98c#%E4%BB%AE%E6%83%B3dom)、[Component](https://qiita.com/ isyuu/items/61268b1e308123ecd98c#component)の3つがあります。
詳細については各リンクを参照してください。


# Netlifyとは
Netlifyは静的サイトのホスティングサービスです。
GitHubやGitLabとも簡単に連携ができるので自動デプロイやJSやAPIを必要としないフォームの作成ができるほか、Netlify Functionsで最近流行りのサーバーレス開発もできるなどフロントエンド開発には十分な機能が揃っています。
ちょっとしたサイトを公開する程度であれば無料枠で使えるので、いろいろなサイトで利用されているのをよく目にします。

# microCMSとは
microCMSはウォンタ株式会社が提供するヘッドレスCMSです。
ウォンタ株式会社は過去に話題になったOsushiという投げ銭サービスを運営していた会社ですね :sob:
microCMSは管理画面から入稿したデータをAPI経由で取得できるので、開発者はフロントエンドに専念することができます。
スキーマの定義はユーザーが自由に登録でき、テキストフィールドやテキストエリアはもちろん、リッチエディタや他コンテンツを参照しに行くようなフィールド定義もできます。
こちらのサービスも無料プランがあるのでちょっとしたことを始めるのには十分いいかもしれません。

# 設計
## ページ構成
ページはHome、Profile、Works、Secret Works（鍵をかけたページ）Contactの5つです。

基本的には名前のとおりのコンテンツが入っていますが、各ページの役割はこのような感じです。

HOME：ホーム
Profile：プロフィールページ
Works：実績ページ
Secret Works：パスワード必須の実績ページ
Contact：お問い合わせページ

## コンテンツ
profileやworksなどのコンテンツはmicroCMSで管理し、API経由でデータを取得・表示させます。

## デプロイ
デプロイはGitHubとNetlifyを連携することで特定のブランチがアップデートされたら実行されるようにします。
また、環境変数も同時に追加されるように設定します。

## フォーム
フォームはNetlify FormsというNetlifyの優れたフォーム機能があるので、こちらを使います。
Netlify FormsはSlackとの連携や登録したメールへの通知機能もあるので非常に便利です。

## クラス名とCSS
クラス名にはBEM記法を用いることにしました。
ReactにBEMは時代遅れな気もしますが、ページの量が少ないのと手短に実装したかったのでBEMを採用し、CSSで実装しました。

ちなみにReactでCSSを使うときはカプセル化するかモジュール化するかの2通りから選ぶのがベターです。

[これからのReactのスタイリングにはStyled Componentsが最高かもしれない](https://blog.spacemarket.com/code/introduce_styled_components/)
[CSSモジュール ― 明るい未来へようこそ](https://postd.cc/css-modules/)

## デザイン

### カラー
ベースカラーを黒にし、アクセントカラーにロイヤルブルーを入れることでクールで落ち着いた印象に仕上げました。

### フォント
フォントは筑紫ゴシックをを用いることでモダンかつシャープな印象を与え、クールに加え美しさを表現しました。

#### 巨大なタイポグラフィ
2020年は巨大なフォントのテキスト配置がトレンドになるそうなので入れてみました。

出処はこちらです
[2020年に流行するWebデザインの最新トレンド14個まとめ](https://webdesign-trends.net/entry/10459)

> 参考
https://ykonishi.tokyo/works/xgCARmagX

# React実装のナレッジ
React実装にはCreate React Appを使いました。
Reactを使い始めるときのデファクトスタンダードですね。

## 環境変数
Create React Appには環境変数を提供するためのパイプラインが存在します。
使い方は、React環境が入っているディレクトリ直下に.envファイルを作成し、下記のように設定を書き込んできます。

```:.env
REACT_APP_API_KEY="xxxxxxxxxxxx"
```

必ず`REACT_APP_`を頭に付けてください。

呼び出すときは下記のようになります。

```js
process.env.REACT_APP_API_KEY
```

React環境をgitで管理する際は、必ず.gitignoreに.envを追記しておくことを忘れないでください。
.envファイルには公にしたくない情報も含まれるので、ホスティングに上げないようにしておきたいからです。

## クリーンアップ処理
ReactでAPI処理を走らせるとき、APIが走っているのにもかかわらずページが変わってしまい下記のような警告がでる場合があります。

```
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
```

こういった警告が出るときはクリーンアップという処理が必要で、実装はこのようになります。

```jsx
useEffect(() => {
  let cleanedUp = false;
  const url = new URL('https://xxxxx.com');
  url.pathname = '/api/v1/profile'
  fetch(url)
  .then(res => res.json())
  .then(res => {
    if (!cleanedUp) setProfile(res)
  })
  .catch(error => {
    console.log(error)
  })
  
  const cleanUp = () => {
    cleanedUp = true;
  };
  return cleanUp;
}, []);
```

クリーンアップはuseEffect内で使用します。
useEffectはアンマウントされるタイミングでreturn処理が走るので、`return cleanUp`とすることでアンマウント時に`cleanedUp`変数がtrueになり`profile`が更新されずに済みます。

## HTMLコードの有効化
HTMLコードをAPIで取得してそのまま表示するとき、そのままではHTMLタグごと表示されてしまうので`dangerouslySetInnerHTML`というのを使ってHTMLに変換させる必要があります。

実装はこのようになります。

```html
<div
  dangerouslySetInnerHTML={{
    __html: profile.biography
  }}
/>
```

名前に`dangerous`とあるように、XSS（クロスサイトスクリプティング）の引き金にもなる可能性があるので使わなくて済む場合は使わないことが推奨されています。

## 遷移時のページ位置

ReactのようなSPAはページが一から読み込まれるのではなく、一部のDOMが更新されるだけなので遷移時にページ位置が変わることがありません。
ですので、ページが変わったら位置をTOPに戻してやる必要があります。

```jsx:ScrollToTop.jsx
const ScrollToTop = () => {
  window.scrollTo(0, 0);
  return null;
};
export default ScrollToTop;
```

このコンポーネントはルーティングの箇所に入れておくことで位置の切り替えができます。

```jsx:Router.jsx
const Router = () => {
  return (
    <BrowserRouter>
      <Route component={ScrollToTop} />
      <Switch>
        <Route exact path="/" component={Home} />
        <Route exact path="/profile" component={Profile} />
      </Switch>
    </BrowserRouter>
  );
};
```

## NotFoundページ
NotFoundページの表示は、ルーティングをひと工夫することで実現させることができます。

```jsx:Router.jsx
const Router = () => {
  return (
    <BrowserRouter>
      <Route component={ScrollToTop} />
      <Switch>
        <Route exact path="/" component={Home} />
        <Route exact path="/profile" component={Profile} />
        <Route path="*" component={NotFoundPage} />
      </Switch>
    </BrowserRouter>
  );
};
```

NotFoundPageコンポーネントはどのパスにも当てはまらなかった場合に表示するように一番下に設置し、`path="*"`とします。
また、必ず一つのコンポーネントが表示されるようにしておかないとNotFoundPageも一緒に表示されてしまうので`Switch`で囲っておきます。


## ローディング
ローディングは`react-spinners`というバリエーション豊富で便利なプラグインがあるので、そちらを使いました。
導入も簡単なのでおすすめです。

> 参考
[React Spinners](https://www.davidhu.io/react-spinners/)

## フォーム作成
フォームはNetlify Formsというのを使うことで簡単に実装できます。

通常のHTMLでは下記のようなコードを入れるだけでいいのですが、Reactの場合は`public/index.html`などにもう一つ別のフォームを入れる必要があります。
Netlifyボットがhtml拡張子以外のファイル内にある、フォームの設定を見に行けないためです。

```jsx:contact.jsx
const Contact = () => {
  return (
    <>
      <form className="contact-form" name="contact" method="post">
        <input type="hidden" name="form-name" value="contact" />
        <label>
          お名前<span className="required-attention">(必須)</span>
          <input className="contact-form__input" type="text" name="username" required/>
        </label>
        <label>
          Email<span className="required-attention">(必須)</span>
          <input className="contact-form__input" type="email" name="email" required/>
        </label>
        <label>
          会社名
        <input className="contact-form__input" type="text" name="company"/>
        </label>
        <label>
         本文<span className="required-attention">(必須)</span>
         <textarea className="contact-form__textarea field__textarea" name="message" required></textarea>
        </label>
        <button className="contact-form__submit" type="submit">送信</button>
      </form>
    <>
  )
}
```

追加する別のフォームは下記のようになります。
contact.jsxで入れたフィールドはこちらにも必ず追加してください。属性はtypeとnameだけで十分です。

```html:public/index.html
<!-- A little help for the Netlify bots if you're not using a SSG -->
<form name="contact" netlify netlify-honeypot="bot-field" hidden>
  <input type="text" name="name" />
  <input type="email" name="email" />
  <input type="text" name="company" />
  <textarea name="message"></textarea>
</form>
```

> 詳しくはこちらを見ていただければと思います。
https://www.netlify.com/blog/2017/07/20/how-to-integrate-netlifys-form-handling-in-a-react-app/

## メニュー
メニューはreact-springを使って実装しました。

独自仕様が多いので使いこなすのには少し時間がかかります。
react-springはReactでアニメーションを表現するためのプラグインで、Hooks用のプラグインでスポンサーやコントリビューターも充実しているのでHooks時代のデファクトスタンダードになるかもしれません。(もうすでにそうなっているかも :thinking: )

> 参考
[React Spring](https://www.react-spring.io)

# 最後に
React + Netlify + microCMSを試しに使ってポートフォリオを作ってみた結果、データ管理やサーバーのことをほとんど考えずにフロントエンドに専念できました。

microCMSについては今回初めて使ってみましたが、スキーマを自分で定義してカスタマイズできるほか、RDBのようにほかのコンテンツ（テーブルのようなもの）を参照しに行く定義もできるので非常に使い勝手が良かったです。
メンバーの権限管理機能やほかサービスとのデータ連携機能もあるので、仕事で使うのにもいいかもしれません。

Web制作で頭を悩ましがちなお問い合わせページでは、Netlifyにある便利なフォーム機能があったおかげで一瞬で片付きました。
Webhooks連携や登録したメールアドレスへの通知機能があるのは嬉しいポイントです。

デザイン周りではなるべくシンプルにし、行間、テキストの両端揃え、フォントにも気を配りました。
また、質素な見た目にちょっとした動きを与えているので、飽きさせない工夫も取り入れました。

フロント面でもバックエンド面でもなかなかイケてるポートフォリオサイトが出来上がったのではないでしょうか。

私自身フロントエンド開発に疎いので、ご意見やアドバイスなどあれば大歓迎です！

[Yuichi Konishiポートフォリオサイト](https://ykonishi.tokyo/)

# 参考
[Netlify](https://www.netlify.com/)
[microCMS](https://microcms.io/)