2
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?

More than 3 years have passed since last update.

TechCommitAdvent Calendar 2019

Day 9

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

Posted at

概要

業務で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にアクセスしてみる
スクリーンショット 2019-12-09 21.18.44.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をリロードしてみる
スクリーンショット 2019-12-09 21.28.32.png
Hello Next.jsと表示されてるのでOK!

ここで、シンタックスエラーを起こしてみよう。

タグの閉じタグを消してみる

  • pages/index.jsの中身
const Index = () => (
  <div>
    <p>Hello Next.js
  </div>
);

export default Index;

ブラウザで再度http://localhost:3000を開いてみる
スクリーンショット 2019-12-09 21.33.33.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にアクセスしてみる
スクリーンショット 2019-12-09 21.42.05.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にアクセスしてみる
スクリーンショット 2019-12-09 21.51.28.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>タグに追加されていない。
スクリーンショット 2019-12-09 22.20.46.png
また、Chromeの開発者モードのConsoleタブを確認するとエラーがでている。
スクリーンショット 2019-12-09 22.21.09.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をみてみよう。

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にアクセスしてみる
スクリーンショット 2019-12-09 22.56.27.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にアクセスしてみる
スクリーンショット 2019-12-09 23.27.36.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にアクセスしてみる
スクリーンショット 2019-12-09 23.30.50.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

今回は、2つの方法について紹介したよ。

  1. 共通のHeaderコンポーネントとして
  2. Layoutコンポーネントとして

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

2
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
2
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?