kakakakakku blog

Weekly Tech Blog : Keep on Learning 👍

Next.js Learn (Basic) を試して学んだ Next.js の基礎の基礎

React を使ってプロトタイプを実装したりすることはあるけど,今まで Next.js を試したことがなかった.そろそろ入門しなければ!という危機感もあり,Next.js Learn (Basic) を一通り試してみた.入門する前と比較すると「Next.js でできること(基礎の基礎)」を把握することができ,非常に良質なコンテンツに仕上がっていた.今回は Next.js Learn (Basic) から学んだことを整理する.Next.js に入門するならまず試してみると良いと思う.

nextjs.org

なお,今回は Next.js 9.2.1 を使えるようにローカル環境を構築した.

$ npm view next version
9.2.1

Next.js Learn (Basic)

Basic では「計9種類」のコンテンツが用意されている.Next.js を使ったサイト内遷移(ルーティング)もあれば,共通コンポーネントの設計もある.ページを静的に最適化したり,styled-jsx を使った CSS-in-JS を実現したり,Now を使ったデプロイも学べる.SSR (Server-Side Rendering) 以外の機能も多く試せる.素晴らしい!

  • Getting Started
  • Navigate Between Pages
  • Using Shared Components
  • Create Dynamic Pages
  • Clean URLs with Dynamic Routing
  • Fetching Data for Pages
  • Styling Components
  • API Routes
  • Deploying a Next.js App

また,手順通りに進めるだけではなく,以下のように動作確認をしながら結果を選ぶクイズも出る.写経をしながら進める価値もあるし,理解度も高まるし,よく考えられているなと思う.

f:id:kakku22:20200206142229p:plain

Next.js アプリケーション

Next.js Learn (Basic) のコンテンツは全て繋がっている.ステップバイステップに学ぶことができるし,コンテンツごとに実装する Next.js アプリケーションも異なる.具体的には,以下の「計4種類」の Next.js アプリケーションを実装する.楽しいぞ!

  • Hello World
  • Blog
  • TV Shows(番組表)
  • Famous Quotes(名言集)

参考までに「Blog アプリ」「TV Shows アプリ」のキャプチャを載せておく.

f:id:kakku22:20200206142252p:plain

f:id:kakku22:20200206142307p:plain

コードは GitHub に公開されている.

github.com

HMR (Hot Module Replacement)

Next.js では,開発環境だとデフォルトで HMR (Hot Module Replacement) が使える.コードを修正すると,すぐにページに反映されるため,実装時のストレスがなく,便利だった.

Link コンポーネント

Next.js では pages ディレクトリ直下に JavaScript ファイルを置くと,自動的に認識される.例えば pages/about.js を作成すれば http://localhost:3000/about にアクセスできるようになる.そして,サイト内遷移を実現するときには Link コンポーネントを使う.今回は「Hello World アプリ」で以下のようなコードを実装した.react-router-dom などを使わずに Next.js で書けるのは良かった.

import Link from 'next/link';

export default function Index() {
  return (
    <div>
      <Link href="/about">
        <a title="About Page">About Page</a>
      </Link>
      <p>Hello Next.js</p>
    </div>
  );
}

Create Dynamic Pages

「Blog アプリ」で2種類の「サイト内遷移」を実装する.最初は以下のような実装となり,Link コンポーネントを使って href props にクエリストリングを追加している.

import Layout from '../components/MyLayout.js';
import Link from 'next/link';

const PostLink = props => (
  <li>
    <Link href={`/post?title=${props.title}`}>
      <a>{props.title}</a>
    </Link>
  </li>
);

export default function Blog() {
  return (
    <Layout>
      <h1>My Blog</h1>
      <ul>
        <PostLink title="Hello Next.js" />
        <PostLink title="Learn Next.js is awesome" />
        <PostLink title="Deploy apps with Zeit" />
      </ul>
    </Layout>
  );
}

遷移したページでは以下のような実装となり,useRouter フックを使ってクエリストリングを取得している.

import { useRouter } from 'next/router'
import Layout from '../components/MyLayout.js'

const Page = () => {
  const router = useRouter()

  return (
    <Layout>
      <h1>{router.query.title}</h1>
      <p>This is the blog post content.</p>
    </Layout>
  )
}

export default Page

ただし,この実装だと URL は http://localhost:3000/post?title=Hello%20Next.js となり,課題が残る.そこで,Next.js の「Dynamic Routes」機能を使う.以下のような実装となり,href props は /p/[id] のように [] を使った記法になる.また as を使って [id] の値を「動的に」設定する.

import Layout from '../components/MyLayout.js'
import Link from 'next/link'

const PostLink = props => (
  <li>
    <Link href="/p/[id]" as={`/p/${props.id}`}>
      <a>{props.id}</a>
    </Link>
  </li>
)

export default function Blog() {
  return (
    <Layout>
      <h1>My Blog</h1>
      <ul>
        <PostLink id="hello-nextjs" />
        <PostLink id="learn-nextjs" />
        <PostLink id="deploy-nextjs" />
      </ul>
    </Layout>
  )
}

遷移したページの実装はあまり変わらないけど,ファイルパスが pages/p/[id].js となり,[] をそのままファイル名に使う点は新しく感じた.URL は http://localhost:3000/p/hello-nextjs となる.

pages
├── about.js
├── index.js
└── p
    └── [id].js

getInitialProps を使う

「TV Shows アプリ」を実装するときに,TVmaze API を使ってテレビ番組データを取得する.そのため,初回ロード時に API を実行する必要があり,今回は getInitialProps を使った.例えば,以下のように実装すると,Index コンポーネントの props から shows を取得できるようになる.

Index.getInitialProps = async function() {
  const res = await fetch('https://api.tvmaze.com/search/shows?q=batman');
  const data = await res.json();

  console.log(`Show data fetched. Count: ${data.length}`);

  return {
    shows: data.map(entry => entry.show)
  };
};

次にテレビ番組データを表示するページの実装をする.以下の実装に console.log があり,このログ出力を動作確認する手順は個人的には気付きが多かった.この console.log のログは「ブラウザ側」「サーバ側」のどちらに出力されるのか?

import Layout from '../../components/MyLayout';
import fetch from 'isomorphic-unfetch';

const Post = props => (
  <Layout>
    <h1>{props.show.name}</h1>
    <p>{props.show.summary.replace(/<[/]?[pb]>/g, '')}</p>
    {props.show.image ? <img src={props.show.image.medium} /> : null}
  </Layout>
);

Post.getInitialProps = async function(context) {
  const { id } = context.query;
  const res = await fetch(`https://api.tvmaze.com/shows/${id}`);
  const show = await res.json();

  console.log(`Fetched show: ${show.name}`);

  return { show };
};

export default Post;

結果としては,トップページから画面遷移をすると「ブラウザ側」にログが表示される.そして http://localhost:3000/p/481 のように URL を直接開くと「サーバ側」にログが表示される.この挙動こそ SSR だよなー!と改めて気づけて良かった.

まとめ

  • Next.js に入門するために Next.js Learn (Basic) を一通り試した
  • Next.js でできることを把握することができる
  • 次は Next.js Learn (Excel) に進むぞ!学ぶことが無限にある!

コンテンツとステップ

最後は付録として,コンテンツごとにステップをまとめておく.多くあるように見えるけど,実際に写経(とコピペ)をしながら進めるだけなら「2,3時間」あれば十分に終わると思う.参考までー!

  • Getting Started
    • Introduction
    • Setup
    • 404 Page
    • Creating Our First Page
    • Handling Errors
    • You are Awesome
  • Navigate Between Pages
    • Introduction
    • Setup
    • Using Link
    • Client-Side History Support
    • Adding Link Props
    • Link is Just a Wrapper Component
    • Link is Simple, but Powerful
  • Using Shared Components
    • Introduction
    • Setup
    • Create the Header Component
    • Using the Header Component
    • The Component Directory
    • The Layout Component
    • Rendering Child Components
    • Using Components
  • Create Dynamic Pages
    • Introduction
    • Setup
    • Adding a list of posts
    • Passing Data via Query Strings
    • useRouter
    • Finally
  • Clean URLs with Dynamic Routing
    • Introduction
    • Setup
    • Dynamic Routing
    • History Awareness
    • Finally
  • Fetching Data for Pages
    • Introduction
    • Setup
    • Fetching Batman Shows
    • Only on the Server
    • Implement the Post Page
    • Fetch Data in Client Side
    • Finally
  • Styling Components
    • Introduction
    • Setup
    • Styling our home page
    • Styles should go inside template strings
    • Styles and Nested Components
    • No Effect for Nested Component
    • Global Styles
    • Global Styles Work
    • What Next
  • API Routes
    • Introduction
    • Setup
    • Creating an API route
    • Fetching API Routes
    • Middlewares
    • Finally
  • Deploying a Next.js App
    • Introduction
    • Setup
    • Deploying to ▲ZEIT Now
    • Deploying to Your Own Environment
    • Build and Start
    • Run two instances
    • Build Once, Run Many Instances
    • Finally