Next チュートリアルやったよ


Gatsbyに引き続きNext.jsのチュートリアルもやってみたので、
覚えたことをまとめておきます。

【公式】https://nextjs.org/learn/basics/create-nextjs-app

##Link

Next.jsのページ内リンクは下記のように呼び出す。

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

~
<Link href="/posts/first-post"><a>this page!</a></Link>
~
```

スタイルを効かせたい時はaタグの方につけることに注意。



## Asset

assetファイルはpublicファイルに置くことで呼び出せる。
呼び出すときのルートはpublicからスタート。

```react

<img src="/vercel.svg" alt="Vercel Logo" className="logo" />
```

robots.txtや認証系なんかもここに置くと便利。



## メタデータ

メタデータを変更したい場合は下記のようにする。


```react
import Head from 'next/head'

~
<Head>
 <title>タイトル</title>
</Head>
~
```

htmlタグをカスタマイズする場合はDocumentコンポーネントを使う。
下記ソースは以下の公式から抜粋。詳しい解読はまた次回。
https://nextjs.org/docs/advanced-features/custom-document

```react
import Document, { Html, Head, Main, NextScript } from 'next/document'

class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const initialProps = await Document.getInitialProps(ctx)
    return { ...initialProps }
  }

  render() {
    return (
      <Html>
        <Head />
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    )
  }
}

export default MyDocument
```


## CSS

Styled-jsxが最初から入っている。もちろんstyled-componentsやemotionも使える。

CSSモジュールを使う際は、.module.cssを末尾につけてcssファイルを生成する。
importするときは下記のように、`styles`で読み込み、 `styles.<class-name>` で使用する。

```react
import styles from './layout.module.css'

export default function Layout({ children }) {
 return <div className={styles.container}>{children}</div>
```

globalモジュールを作成する場合は、

page/_app.js ファイルを作成し、下記のような高階コンポーネントを作成し、そこでCSSモジュールを読み込む。

```react:page/_app.js
import '../styles/global.css'
export default function App({ Component, pageProps }){
	return <Component {...pageProps} />
}
```

_app.js追加はホットリロードに対応していないので、サーバーを立ち上げ直す。



postCSSの設定をカスタマイズする場合は、一番上の階層に`postcss.config.js`を作成する。

autoprefixerは最初からNextに組み込まれている。

Sassのモジュールも使用可能(.module.scss,module.sassなど)
使用する際はsassをinstallする必要がある。



## pre-rendering

Nextでは、事前にHTMLを生成する**pre-rendering**を行なっている。
生成されたページはhydrationされる。

データを使用して静的ページを生成する場合(外部APIのfetch,データベースへのアクセス等)、
`getStaticProps`を使用する。

getStaticPropsを使用することで、pre-renderする際にfetchしてからHTMLを生成するようになる。



## データをもとに静的ページを生成する場合の例

posts以下にmdファイルを置いた場合

lib/posts.jsに下記を貼る

```js:lib/posts.js
import fs from fs
import path from 'path'
import matter from 'gray-matter'

const postsDirectory = path.join(process.cwd(), 'posts')

export function getSortedPostsData() {
  const fileNames = fs.readdirSync(postsDirectory)
  const allPostsData = fileNames.map(fileName => {
    const id = fileName.replace(/\.md$/, '')

    const fullPath = path.join(postsDirectory, fileName)
    const fileContents = fs.readFileSync(fullPath, 'utf8')

    const matterResult = matter(fileContents)

    return {
      id,
      ...matterResult.data
    }
  })

  return allPostsData.sort((a, b) => {
    if (a.date < b.date) {
      return 1
    } else {
      return -1
    }
  })
}

```

gray-matterはmdファイルのメタデータをjsonに変換してくれるパッケージ。
sortすることによって、記事の順番を日付が最新のものが先頭に来るように調節している。



```react
import Head from 'next/head'
import Layout, { siteTitle } from '../components/layout'
import utilStyles from '../styles/utils.module.css'
import { getSortedPostsData } from '../lib/posts'

export default function Home({ allPostsData }) {
  return (
    <Layout home>
      <Head>
        <title>{siteTitle}</title>
      </Head>
      <section className={`${utilStyles.headingMd} ${utilStyles.padding1px}`}>
      <h2 className={utilStyles.headingLg}>Blog</h2>
        <ul className={utilStyles.list}>
          {allPostsData.map(({ id, date, title }) => (
            <li className={utilStyles.listItem} key={id}>
              {title}
              <br />
              {id}
              <br />
              {date}
            </li>
          ))}
        </ul>
      </section>
    </Layout>
  )
}

export async function getStaticProps() {
  const allPostsData = getSortedPostsData()
  return {
    props: {
      allPostsData
    }
  }
}

```

gatsbyに比べて全部手動で追加/加工しないといけないような感じ。
でもその分柔軟性がありそう。



## リクエスト時のデータフェッチ

この場合はSSRを試す必要がある。

SSRではgetStaticPropsの代わりに`getServerSideProps`を使用する。

dataをPre-renderingする必要がないのであれば、CSRでも対応できる。
ダッシュボードなどのSEOが関係ない部分ではオススメ

Nextでは**SWR**というデータ取得用のreactフックが用意されているので、CSRでやる場合はオススメ



## Dynamic Route

動的ルート生成するには、 pages/postsの下に　`[` `]`で囲ったファイルを作る　`[id].js`など。

`getStaticPaths()`を使用する。



lib/posts.jsに下記を追加する。

```react:lib/posts.js
export function getAllPostIds() {
  const fileNames = fs.readdirSync(postsDirectory)
  return fileNames.map(fileName => {
    return {
      params: {
        id: fileName.replace(/\.md$/, '')
      }
    }
  })
}

export function getPostData(id) {
  const fullPath = path.join(postsDirectory, `${id}.md`)
  const fileContents = fs.readFileSync(fullPath, 'utf8')

  const matterResult = matter(fileContents)

  return {
    id,
    ...matterResult.data
  }
}

```

`getAllPostIds`にて守らなければならないのは、returnが必ずオブジェクトになっていること、そして`params`があり、そのオブジェクトには[]の中で指定したkeyがあること。

idを受けてファイルデータを取得する関数も作っておく。

pages/post/[id].jsは下記。

```react:pages/post/[id].js
import Layout from '../../components/layout'
import { getAllPostIds, getPostData } from '../../lib/posts'

export default function Post({ postData }) {
  return (
    <Layout>
      {postData.title}
      <br />
      {postData.id}
      <br />
      {postData.date}
    </Layout>
  )
}


export async function getStaticPaths() {
  const paths = getAllPostIds()
  return {
    paths,
    fallback: false
  }
}

export async function getStaticProps({ params }) {
  const postData = getPostData(params.id)
  return {
    props: {
      postData
    }
  }
}

```

getStaticPathではidのリストとfallbackを指定する。
getStaticPropsで作成したデータがpageのpropsとして渡される。
idからファイルデータを取得するgetPostDataを使用し、propsとして渡す。



## mdのメタデータ以外の取得

```
npm install remark remark-html
```

そして、/lib/postsで

```react:lib/posts
export async function getPostData(id) {
  const fullPath = path.join(postsDirectory, `${id}.md`)
  const fileContents = fs.readFileSync(fullPath, 'utf8')

  const matterResult = matter(fileContents)

  const processedContent = await remark()
    .use(html)
    .process(matterResult.content)
  const contentHtml = processedContent.toString()

  return {
    id,
    contentHtml,
    ...matterResult.data
  }
}

```

あとは[id].js内でpostData.contentHtmlとして取得すればOK。

## Indexにリンクを貼る場合

```react
<Link href="/posts/[id]" as="/posts/ssg-ssr">
  <a>...</a>
</Link>
```

この形で用いる。他は[id].jsと同じ。

```react
          {allPostsData.map(({ id, date, title }) => (
            <li className={utilStyles.listItem} key={id}>
              <Link href="/posts/[id]" as={`/posts/${id}`}>
                <a>{title}</a>
              </Link>
              <br />
              <small className={utilStyles.lightText}>
                <Date dateString={date} />
              </small>
              {id}
              <br />
              {date}
            </li>
          ))}
...


export async function getStaticProps() {
  const allPostsData = await getSortedPostsData()
  return {
    props: {
      allPostsData
    }
  }
}

```

ファイル名に ...を使うこともできる。
`pages/posts/[...id].js`は /posts/a, /posts/a/b などにマッチする。

getStaticPathsで/posts/a/b/cを作成するには配列を用いる。

```react
return [
  {
    params: {
      // Statically Generates /posts/a/b/c
      id: ['a', 'b', 'c']
    }
  }
  //...
]
```



## 404ページの作成

page/404.jsを作ればOK。



## APIルートの作成

`pages/api`ファイルの中に以下のようなfunctionを作成する。

```react
export default (req, res) => {
  res.status(200).json({ text: 'Hello' })
}
```

あとは/api/ファイル名にアクセスするだけ! 
getStaticPropsやgetStaticPathsではfetchできないので注意。
なぜならこの２つはサーバー側でのみ走るため。



## Gatsbyと比べてみて
Nextは公式でexampleが豊富に準備されているのでありがたいです。
https://github.com/vercel/next.js/tree/2cd691050af6b2bb75d6e6ab7c63efcea7074f1b/examples

Gatsbyはスターターが優秀で、スターターを元に基本的なことはすぐできちゃう!
という印象を受けました。
Gatsbyのように基本に特化していない分Nextは柔軟性があるように感じました。
Gatsbyは複雑な処理でなければ内部で使われているパッケージを直接扱わないが、nextでは直接扱う場面が多かったことからもわかります。（grey-matterやremarkなど)

それからgatsbyはgraph.qlが使われる前提の設計なので、好みが分かれそうだと思いました。

スピード優先・そこまで複雑なことをしない/graph.qlを手軽に扱いたいのであればGatsby、規模がそれなりにあったり複雑である or Graph.qlに縛れられたくないのであればNextかな、という感じです。
これから実際に色々使用してみて、また比較をまとめれたらと思います。