LoginSignup
7
2

More than 3 years have passed since last update.

Next.js + redux-persist の落とし穴に注意しよう

Posted at

TL; DR

Next.js で redux-persist を使って store を LocalStorage に保存したい時、_app.js<Component {...pageProps} /> 全体を PersistGate で囲うのではなく、個々の Component を PersistGate で囲うようにしよう

※ 以下の記事に既に同様のことが書かれています。参考にさせていただきました。
https://qiita.com/miyabiya/items/14e4f133d5df5d53cd77

落とし穴

Next.js で構築していた自分のサイトが Twitter Cards の生成に失敗しているのに気づいた。よく確認すると SSR で meta タグを吐き出すのに失敗している。
そのときの構成はこんな感じ。

pages/_app.tsx
import App, { Container } from "next/app";
import Layout from "../layouts/page-layout";
import { Provider } from "react-redux";
import store, { persistor } from "../store/store";
import { PersistGate } from "redux-persist/integration/react";

export default class MyApp extends App {
  render () {
    const { Component, pageProps, router } = this.props;

    return (
      <Container>
        <Provider store={store}>
          <PersistGate loading={null} persistor={persistor}>
            <Component {...pageProps} key={router.route} />
            <Layout />
          </PersistGate>
        </Provider>
      </Container>
    );
  }
}
pages/_documents.tsx
import Document, { Head, Main, NextScript } from "next/document";

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

  render() {
    return (
      <html lang="ja">
        <Head>
          <link rel="shortcut icon" href="/static/favicon.ico" />
          <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
          <meta name="theme-color" content="#000000" />
          <meta name="description" content="HelloRusk Official Website." />
          <meta property="og:image" content="https://hellorusk.net/static/mika_square.png" />
          <meta name="twitter:card" content="summary" />
          <meta name="twitter:site" content="@HelloRusk" />
          <meta name="twitter:image" content="https://hellorusk.net/static/mika_square.png" />
          <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Noto+Sans+JP:300&display=swap" />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </html>
    );
  }
}
pages/それぞれのページ.tsx
import Head from "next/head";
import { hogemode, fugamode } from "../store/actions";
import { ModeState, ActionTypes } from "../store/types";
import HogeComponent from "../components/hoge";
import { connect } from "react-redux";

interface HogeProps {
  isHogekMode: boolean,
  hogemode: () => ActionTypes,
  fugamode: () => ActionTypes
}

const Index: React.FC<HogeProps> = props => {
  const handleChange = () => {
    props.isHogeMode ? props.hogemode() : props.fugamode();
  };

  return (
    <div>
      <Head>
        <title key="title">title</title>
        <meta property="og:title" content="このページのtitle" /> 
        <meta property="og:url" content="このページのurl" /> 
        <meta property="og:description" content="このページの概要" />
      </Head>
      // 何らかの store を持つ Component
      <HogeComponent
        checked={props.isHogeMode}
        onChange={() => handleChange()}
      />
    </div>
  );
};

const mapStateToProps = (state: ModeState) => {
  return {
    isHogeMode: state.isHogeMode,
  };
};

const mapDispatchToProps = {
  hogemode,
  fugamode
};

export default connect(mapStateToProps, mapDispatchToProps)(Index);

_app.js を見ればわかるように、全体を PersistGate で囲っている。
これによって、PersistGate 配下の Component は、LocalStorage にアクセスして状態を取り出すのを待ってからレンダリングが行われるようになる。画面表示的には何も問題はない。

問題は、これによって それぞれのページ.tsx にある <Head> 内の meta タグ

      <Head>
        <title key="title">title</title>
        <meta property="og:title" content="このページのtitle" /> 
        <meta property="og:url" content="このページのurl" /> 
        <meta property="og:description" content="このページの概要" />
      </Head>

この部分が SSR されなくなっていたことにある。よって、GET で得られる html にも meta タグが正しく記述されておらず、Twitter Cards などのOGP設定に失敗していた。

対策

PersistGate の粒度を上げればよい。
すなわち、大雑把に _app.js 全体を囲うのではなく、store を使っている Component それぞれに PersistGate を当てればよい。
上の例では、HogeComponentPersistGate で囲うことになる。

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