Next.jsで、同じ変数なのに属性内とテキスト内とで違う値になる

## 環境
- Next.js 9.4.4
- React 16.13.1

## 再現手順
[Next.js](https://nextjs.org/)で、下記のようなコンポーネントを定義します。そして`next dev`で開発サーバを立ち上げるか、`next build && next export`で静的サイト生成して、サイトを表示します。

```jsx:pages/index.js
export default function Home() {
  const now = Number(new Date());
  return (
    <>
      <p><a href={now}>{now}</a></p>
    </>
  )
}
```

このときの`now`変数で表示される値が、href属性とaタグ内部テキストとで**同じ変数なのに異なる値を取ってしまいます**。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50391/9f71716c-7d5a-5035-fc09-b39703f236fb.png)

ふたつの値は、それぞれ下記のものになっています。

- href属性内の値は、サーバサイドでのページ生成時の値(静的サイトのビルド時の値)
- テキストは、クライアントサイドでのページ表示時の値

開発サーバ立ち上げ時はブラウザのコンソールに`prop did not match. Server: ..., Client: ...`という警告が出るので気づきやすいですが、静的サイト生成時だと表示されません。逆に、静的サイト生成の方で試すとエラーは出ませんが違いが大きいため分かりやすいです。

適当にステートを更新して明示的に再描画すると、それ以降ふたつの値はちゃんと一致するようになります。

```jsx:pages/index.js
export default function Home() {
  const now = Number(new Date());
  const [_, setHoge] = React.useState();

  return (
    <>
      <p><a href={now}>{now}</a></p>
      <input type="button" value="refresh" onClick={()=>{setHoge(Math.random())}} />
    </>
  )
}
```

また、Productionモードで動作させたときもこのような現象は発生しないようです。

## 原因
どうやらNext.jsの[Automatic Static Optimization](https://next-site-git-new-docs.zeit.now.sh/docs/advanced-features/automatic-static-optimization)という機能が原因のようです。サーバサイドとクライアントサイド両方で生成が可能な変数で、かつサーバとクライアントとで値が変わってしまう場合に、このような現象が発生するようです。

この問題は、[getStaticProps](https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation)を使うと値が一致するようになります。

```jsx:pages/index.js
export default function Home({ now2 }) {
  return (
    <>
      <p><a href={now2}>{now2}</a></p>
    </>
  )
}

export async function getStaticProps() {
  const now2 = Number(new Date());
  return { props: { now2 } }
}
```

またはAutomatic Static Optimizationは[getInitialPropsという機能](https://nextjs.org/docs/api-reference/data-fetching/getInitialProps)を使うことで無効になります。

```jsx:pages/index.js
export default function Home({ now2 }) {
  return (
    <>
      <p><a href={now2}>{now2}</a></p>
    </>
  )
}

Home.getInitialProps = async (_) => {
  return { now2: Number(new Date()) };
}
```

ただしこれらの方法を使うと、**静的サイトのビルド時に(サーバサイドで)生成された値が更新される事はありません**。静的サイト生成を使う場合は特に困りますね。

もし静的サイト生成を使わない場合(サーバサイドレンダリングする場合)は、[getServerSideProps](https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering)を使えば良いようです。

```jsx:pages/index.js
export default function Home({ now2 }) {
  return (
    <>
      <p><a href={now2}>{now2}</a></p>
    </>
  )
}

export async function getServerSideProps() {
  const now2 = Number(new Date());
  return { props: { now2 } }
}
```

### 解決方法
`componentDidMount`に引っ掛けることで、サーバサイドで描画されないように(クライアントサイドで一度レンダリングが走った後に、実際のDOMが描画されるように)します。`useEffect()`を使う場合は下記のような感じです。

```jsx:pages/index.js
export default function Home() {
  const now = Number(new Date());

  const [isMounted, setIsMounted] = React.useState(false);
  React.useEffect(() => setIsMounted(true));
  if (!isMounted) return <>Loading...</>;

  return (
    <>
      <p><a href={now}>{now}</a></p>
    </>
  )
}
```

## 参考
- [Attribute not updated after re\-hydratation · Issue \#8643 · vercel/next\.js](https://github.com/vercel/next.js/issues/8643)
- [Q: How to execute code client\-side only · Issue \#2473 · vercel/next\.js](https://github.com/vercel/next.js/issues/2473)