Next.jsの公式チュートリアルをやってみた（1/3）

# 概要
業務でNext.jsを触る機会を得たため、まずは下記チュートリアルをやってみました。
チュートリアルの章ごとの備忘録をまとめます。
https://nextjs.org/learn/basics/getting-started

# Getting Started

## Introduction
Next.js良いぞ！と機能紹介しているページ。一旦読み飛ばす

## Setup
作業用の`hello-next`ディレクトリを作成し、以降ここで作業。
まずは、npmコマンドで必要なライブラリをインストールし、
ビュー用のファイルを置くことになるpagesディレクトリを作成します。

```
$ mkdir hello-next
$ cd hello-next
$ npm init -y
$ npm install --save react react-dom next
$ mkdir pages
```

- 作業後のディレクトリ構成

```
$ ll
drwxr-xr-x  524 aota  staff    16K 12  9 21:00 node_modules/
-rw-r--r--    1 aota  staff   263K 12  9 21:00 package-lock.json
-rw-r--r--    1 aota  staff   324B 12  9 21:00 package.json
drwxr-xr-x    2 aota  staff    64B 12  9 21:08 pages/
```

- package.jsonの中身

```
$ cat package.json 
{
  "name": "hello-next",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "next": "^9.1.4",
    "react": "^16.12.0",
    "react-dom": "^16.12.0"
  }
}
```

上記package.jsonのscriptsを下記に書き換えるよう指示されるので置換する。

```
"scripts": {
  "dev": "next",
  "build": "next build",
  "start": "next start"
}
```

- 置換後のpackage.jsonの中身

```
$ cat package.json 
{
  "name": "hello-next",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "next": "^9.1.4",
    "react": "^16.12.0",
    "react-dom": "^16.12.0"
  }
}
```

あとは、下記コマンド実行で準備完了！

```
$ npm run dev
```

ブラウザで`http://localhost:3000`にアクセスしてみる
<img width="421" alt="スクリーンショット 2019-12-09 21.18.44.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/438524/bb654da0-e1d9-cd5b-ece1-2adb62f4071d.png">
何が表示された？と聞かれるので、404だよーと選択し次へ。

## 404 Page

404表示されてるかい？それでOKだ

## Creating Our First Page

先程作ったpagesディレクトリ配下にindex.jsを作る

```
$ vi pages/index.js
```

- pages/index.jsの中身

```
const Index = () => (
  <div>
    <p>Hello Next.js</p>
  </div>
);

export default Index;
```

ブラウザで先程開いていた`http://localhost:3000`をリロードしてみる
<img width="103" alt="スクリーンショット 2019-12-09 21.28.32.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/438524/d21328d1-6661-d721-b54d-d7307a59eba9.png">
Hello Next.jsと表示されてるのでOK！

ここで、シンタックスエラーを起こしてみよう。<p>タグの閉じタグを消してみる

- pages/index.jsの中身

```
const Index = () => (
  <div>
    <p>Hello Next.js
  </div>
);

export default Index;
```

ブラウザで再度`http://localhost:3000`を開いてみる
<img width="230" alt="スクリーンショット 2019-12-09 21.33.33.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/438524/927b5857-a107-c661-c4c9-a709573dcb8c.png">

何が表示された？と聞かれるので、Syntaxエラーがでたよと選択し次へ

## Handling Errors
Next.jsではデフォルトでブラウザにエラーを出力するよ。
エラーを直したらリロードしなくてもページが再レンダリングされるんだ。

## You are Awesome
ファーストステップはこれでおしまい！

# Navigate Between Pages
単体のページは、できたので今度は複数のページを作ろう。

先程作ったpagesディレクトリ配下にabout.jsを作る

```
$ vi pages/about.js
```

- pages/about.jsの中身

```
export default function About() {
  return (
    <div>
      <p>This is the about page</p>
    </div>
  );
}
```

ブラウザで`http://localhost:3000/about`にアクセスしてみる
<img width="161" alt="スクリーンショット 2019-12-09 21.42.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/438524/b3b99015-e5bc-5fd6-0e78-d8cb35f694ba.png">
うむ、ちゃんと表示された。

次は、これらのページをつなげてみる
ただ`<a>`タグで書いちゃうと、サーバーとの通信が発生するよね。
クライアントサイドでナビゲーションしたいから、`<Link>`タグを使うよ。
使い方は次回。

## Using Link
作っていた`pages`配下の`index.js`を`<Link>`タグを使うように書き換える。

```
$ vi pages/index.js
```

- pages/index.jsの中身

```
// This is the Link API
import Link from 'next/link';

const Index = () => (
  <div>
    <Link href="/about">
      <a>About Page</a>
    </Link>
    <p>Hello Next.js</p>
  </div>
);

export default Index;
```

ブラウザで`http://localhost:3000/about`にアクセスしてみる
<img width="111" alt="スクリーンショット 2019-12-09 21.51.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/438524/fc1c25b0-5b33-6ffc-d1ae-5759f621b5e9.png">
`About Page`をクリックしてaboutページに遷移後、ブラウザの戻るボタンで戻っても
正常にindexページに遷移することが確認できる。
（サーバーにリクエストがいっていないのは、Chromeの開発者モードのNetworkタブから確認できる）

## Client-Side History Support
先程、ブラウザの戻るボタンで戻れたのは、
`location.history`を`<Link>`タグがハンドリングしてたからなんだ。

## Adding Link Props
属性値を追加したいときあるよね。
title属性を追加したい場合は、こうするはずだ。

- pages/index.jsの中身

```
// This is the Link API
import Link from 'next/link';

const Index = () => (
  <div>
    <Link href="/about">
      <a title="About Page">About Page</a>
    </Link>
    <p>Hello Next.js</p>
  </div>
);

export default Index;
```

これは正常にレンダリングされる。では下記の場合はどうか。

- pages/index.jsの中身

```
// This is the Link API
import Link from 'next/link';

const Index = () => (
  <div>
    <Link href="/about" title="About Page">
      <a>About Page</a>
    </Link>
    <p>Hello Next.js</p>
  </div>
);

export default Index;
```

ブラウザで`http://localhost:3000/about`にアクセスしてみる
Chromeの開発者モードのElementsタブを確認してもtitle属性は、`<a>`タグに追加されていない。
<img width="240" alt="スクリーンショット 2019-12-09 22.20.46.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/438524/5cf4a803-bc07-9b94-ea20-d9fa481ebaca.png">
また、Chromeの開発者モードのConsoleタブを確認するとエラーがでている。
<img width="814" alt="スクリーンショット 2019-12-09 22.21.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/438524/7ce251eb-0f32-0f74-56d9-b47e1f057689.png">
何が表示された？と聞かれるので、title属性はないしConsoleでエラーがでたよと選択し次へ

## Link is Just a Higher Order Component (HOC)
実際、title属性を追加しても`<Link>`タグには、なんの影響もないんだ。
なぜなら、hrefか近い属性しか`<Link>`タグは許容していないからだ。
（`<Link>`タグもとい`next/link`がHOCというもので、通常のコンポーネントではないということ。）

属性を追加したい場合は、その下のコンポーネント、今回であれば`<a>`タグに渡せば良い。
また`<Link>`タグの子コンポーネントとして必要なことは、onClick属性が使えることだけだ。

## Link is Simple, but Powerful
これまで基本的な`<Link>`タグの使い方をみてきたけど、
今後のレッスンではもっと面白い使い方を紹介するよ。
それまでは、[Next.js Routing documentation](https://nextjs.org/docs/#routing)をみてみよう。

# Using Shared Components

## Introduction
共通のHeaderコンポーネントを作って、複数のページで読み込んでみよう

## Create the Header Component
componentsディレクトリを作成し、配下にHeader.jsを作成する。

```
$ mkdir components
$ vi components/Header.js
```

- components/Header.jsの中身

```
import Link from 'next/link';

const linkStyle = {
  marginRight: 15
};

const Header = () => (
  <div>
    <Link href="/">
      <a style={linkStyle}>Home</a>
    </Link>
    <Link href="/about">
      <a style={linkStyle}>About</a>
    </Link>
  </div>
);

export default Header;
```

## Using the Header Component
pages/index.jsとpages/about.jsを編集し、先程作成したHeaderコンポーネントを読みこむ。

- pages/index.jsの中身

```
import Header from '../components/Header';

export default function Index() {
  return (
    <div>
      <Header />
      <p>Hello Next.js</p>
    </div>
  );
}
```

- pages/about.jsの中身

```
import Header from '../components/Header';

export default function About() {
  return (
    <div>
      <Header />
      <p>This is the about page</p>
    </div>
  );
}
```

ブラウザで`http://localhost:3000`にアクセスしてみる
<img width="115" alt="スクリーンショット 2019-12-09 22.56.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/438524/8eefd95c-6380-4f03-b80c-b51cce073088.png">
Headerコンポーネントが読み込まれ、Home、Aboutそれぞれで遷移することが確認できる。

ここでちょっとした変更をしてみよう。
1. `$ npm run dev`で起動したアプリケーションをSTOPする。（`Ctrl`+`C`）
2. componentsディレクトリをcompsにリネームしてみる。
3. pages/index.jsおよびpages/about.jsで、Headerコンポーネントの読み込み先を`../comps/Header`に変更する。
4. アプリケーションを再起動する（`$ npm run dev`）

- pages/index.jsの中身

```
import Header from '../comps/Header';

export default function Index() {
  return (
    <div>
      <Header />
      <p>Hello Next.js</p>
    </div>
  );
}
```

- pages/about.jsの中身

```
import Header from '../comps/Header';

export default function About() {
  return (
    <div>
      <Header />
      <p>This is the about page</p>
    </div>
  );
}
```

何が表示された？と聞かれるので、問題なく動いたと選択し次へ

## The Component Directory
ちゃんと動くよね。コンポーネントを置くディレクトリは好きな名前にしていいんだ。
特別な名前が必要なのは、`/pages`ディレクトリと、`/public`ディレクトリだけなんだ。
もちろん、コンポーネントは`/pages`ディレクトリ配下にも作成できる。
ただ今回そうしなかったのは、Headerコンポーネントへの直接のリンクが不要だからだ。
（`http://localhost:3000/Header`にアクセスするのは不要。）

※ ここでcompsは、componentsにリネームして戻しておく。アプリケーションの再起動も忘れずに

## The Layout Component
共通のCSSスタイルをあてたいときあるよね。
共通のレイアウトコンポーネントを作っていくよ。

まず、componentsディレクトリ配下にMyLayout.jsを作成するよ。

- components/MyLayout.jsの中身

```
import Header from './Header';

const layoutStyle = {
  margin: 20,
  padding: 20,
  border: '1px solid #DDD'
};

const Layout = props => (
  <div style={layoutStyle}>
    <Header />
    {props.children}
  </div>
);

export default Layout;
```

そして、このレイアウトコンポーネントを使うためにpages/index.jsとpages/about.jsを編集する。

- pages/index.jsの中身

```
import Layout from '../components/MyLayout';

export default function Index() {
  return (
    <Layout>
      <p>Hello Next.js</p>
    </Layout>
  );
}
```

- pages/about.jsの中身

```
import Layout from '../components/MyLayout';

export default function About() {
  return (
    <Layout>
      <p>This is the about page</p>
    </Layout>
  );
}
```

ブラウザで`http://localhost:3000/about`にアクセスしてみる
<img width="470" alt="スクリーンショット 2019-12-09 23.27.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/438524/afd329b6-a185-b263-e10c-88f8aac4b6d5.png">
レイアウトコンポーネントで指定した`border: '1px solid #DDD'`の枠線があることが確認できた。

次に、components/MyLayout.jsから`{props.children}`を削除してみよう。

- components/MyLayout.jsの中身

```
import Header from './Header';

const layoutStyle = {
  margin: 20,
  padding: 20,
  border: '1px solid #DDD'
};

const Layout = props => (
  <div style={layoutStyle}>
    <Header />
  </div>
);

export default Layout;
```

ブラウザで`http://localhost:3000/about`にアクセスしてみる
<img width="470" alt="スクリーンショット 2019-12-09 23.30.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/438524/5dfad849-0eed-1b79-4af1-292d8853f693.png">
何が表示された？と聞かれるので、コンテンツが消えたと選択し次へ

## Rendering Child Components
`{props.children}`を削除すると、Layoutコンポーネントの中においたコンテンツはレンダリングされないんだ。
Layoutコンポーネントの作成方法をひとつ紹介したけど、他にも作成する方法があるよ。

### Method 1 - Layout as a Higher Order Component

```
// components/MyLayout.js

import Header from './Header';

const layoutStyle = {
  margin: 20,
  padding: 20,
  border: '1px solid #DDD'
};

const withLayout = Page => {
  return () => (
    <div style={layoutStyle}>
      <Header />
      <Page />
    </div>
  );
};

export default withLayout;
```

```
// pages/index.js

import withLayout from '../components/MyLayout';

const Page = () => <p>Hello Next.js</p>;

export default withLayout(Page);
```

```
// pages/about.js

import withLayout from '../components/MyLayout';

const Page = () => <p>This is the about page</p>;

export default withLayout(Page);
```

### Method 2 - Page content as a prop

```
// components/MyLayout.js

import Header from './Header';

const layoutStyle = {
  margin: 20,
  padding: 20,
  border: '1px solid #DDD'
};

const Layout = props => (
  <div style={layoutStyle}>
    <Header />
    {props.content}
  </div>
);

export default Layout;
```

```
// pages/index.js

import Layout from '../components/MyLayout.js';

const indexPageContent = <p>Hello Next.js</p>;

export default function Index() {
  return <Layout content={indexPageContent} />;
}
```

```
// pages/about.js

import Layout from '../components/MyLayout.js';

const aboutPageContent = <p>This is the about page</p>;

export default function About() {
  return <Layout content={aboutPageContent} />;
}
```

## Using Components
今回は、２つの方法について紹介したよ。
1. 共通のHeaderコンポーネントとして
2. Layoutコンポーネントとして

コンポーネントは、スタイル適用やページのLayoutなどに使えるんだ。
また、NPMモジュールから読み込む方法もあるよ。