クモのようにコツコツと

フロントエンドエンジニア イイダリョウの技術ブログ。略称「クモコツ」

【React】ジャンプ率ジェネレーターをNext.jsで作り直した(Next.js + TypeScript + CSS in JS)

ジャンプ率ジェネレーターの続きです。以前、React環境でいったん完成しましたが、この時実現できなかったページごとのOGP設定を実現すべく、前回作成したNextスターターキットで再度作り直しました。これによってReact環境とNext環境ではCSS in JS(Styled-components)の挙動に差異があることもわかりました。それではいきましょう!

【目次】

※参考:前回記事
【React】styled-componentsにフックの値を設定し、動的にスタイルを変更(ジャンプ率ジェネレーター完成!) - クモのようにコツコツと

※参考:ReactでWebアプリを作るシリーズまとめ
qiita.com

前回のおさらい

React環境でジャンプ率ジェネレーターを作った https://cdn-ak.f.st-hatena.com/images/fotolife/i/idr_zz/20210406/20210406071313.jpg

ReactのSPAのため、ページごとのOGP設定はうまくいかなかった。

※参考:前回記事
【React】styled-componentsにフックの値を設定し、動的にスタイルを変更(ジャンプ率ジェネレーター完成!) - クモのようにコツコツと


その後、Nextスターターキットを作成し、ページごとのOGP設定ができるようになった。

※参考:【React】ReactでWebアプリを作るシリーズまとめ(随時更新) - Qiita

ジャンプ率ジェネレーターもNext環境で作り直してOGPを設定したい。

作ったもの

文字ジャンプ率ジェネレーター(Next環境で作り直した)
f:id:idr_zz:20210504102627j:plain

ツマミを動かすと行長、行間、ジャンプ率の変更が下のサンプルに反映される
f:id:idr_zz:20210504102733j:plain
CSSの文字設定値も表示されるので適切な設定値を検討することができる。


プレビュー

jump-rate-generator-2.vercel.app

Aboutページ(こちらにも固有のOGPが表示される!)

jump-rate-generator-2.vercel.app

ソースコード

※参考:GitHub - ryo-i/jump-rate-generator-2

環境構築

React版のジャンプ率ジェネレータのときと同じ手順

  • Nextスターターキットのリポジトリをgit cloneでクローン(アプリ名「jump-rate-generator-2」は最初から指定)
  • cdコマンドでフォルダに移動しnpm installでパッケージをインストール
  • GitHubで新リポジトリ「jump-rate-generator-2」を作成
  • 「.git」フォルダを削除してgit initで再作成
  • git remote add originでリポジトリと紐づける

※参考:【React】文字ジャンプ率ジェネレーターを作るシリーズ始動!(環境構築編) - クモのようにコツコツと

ファイル構成

React版とNext版のファイル構成を比較
f:id:idr_zz:20210504104631j:plain

  • React版は「/src」の中にコンポーネント、モジュール、スタイル、データ全てを入れ、静的なファイルは「/public」にある
  • Next版ではコンポーネントからページ設定は「/pages」に分け、その他は「/components」、他のフォルダはルート直下に

ソースコード(React版)

※参考:GitHub - ryo-i/jump-rate-generator

ソースコード(Next版)

※参考:GitHub - ryo-i/jump-rate-generator-2

共通テキスト設定(data.json)

複数ページにわたるテキストを「/data/data.json」に設定

{
    "head": {
        "url": "https://jump-rate-generator-2.vercel.app/"
    },
    "header": {
        "title":"ジャンプ率ジェネレーター",
        "text": "文字ジャンプ率を動的に変えることができます"
    },
   // 後略

トップページ(index.tsx)

トップページは「/pages/index.tsx」に設定

各種インポート

import Head from 'next/head';
import Header from '../components/Header';
import Inner from '../components/Inner';
import Footer from '../components/Footer';
import Data from '../data/data.json';
  • Next.jsからHeadをインポート
  • HeaderInnerFooterコンポーネントをインポート
  • data.jsonからテキストをインポート

HeaderFooterコンポーネントはNextスターターキットと変更なし

※参考:【React】Nextスターターキットを作った-2. コンポーネント編(Next + TypeScript + CSS in JS) - クモのようにコツコツと

Innerコンポーネントについては後述


テキスト設定

const headerTitle = Data.header.title;
const headerText = Data.header.text;
const pageTitle = 'ジャンプ率ジェネレーター';
const pageText = '行長、行間、ジャンプ率などを変えてみてください。';
  • headerTitleheaderTextDataから取得
  • pageTitlepageTextはベタガキ

Homeコンポーネント作成

function Home() {
  return (
    <>
      <Head>
        <title>{ headerTitle }</title>
        <meta name="description" content={ headerText } />
        <meta property="og:title" content={ headerTitle } />
        <meta property="og:description" content={ headerText } />
      </Head>
      <Header />
      <main>
        <h1>{ pageTitle }</h1>
        <p dangerouslySetInnerHTML={{ __html: pageText }}></p>
        <Inner />
      </main>
      <Footer />
    </>
  )
}

React版はreact-routerとhelmetを使ってheadタグを設定したがSPAの動的な変更のためOGPに反映されなかった。

※参考:【React】OGPはつらいよ ーSPAでの動的OGP・失敗編ー(Reactアプリスターターキット) - クモのようにコツコツと

Next版ではindex.tsxに直接OGP設定を書くことで静的なheadタグが生成される!

※参考:【React】Nextスターターキットを作った-3. ページファイル編(Next + TypeScript + CSS in JS) - クモのようにコツコツと


最後にHomeコンポーネントをエクスポート

export default Home;

Dev-toolsの「Sources」を見ると静的なOGP設定になっていることがわかる! f:id:idr_zz:20210504112034j:plain

※参考:ジャンプ率ジェネレーター

Aboutページ(about.tsx)

Aboutページは「/pages/about.tsx」にある

各種インポート

import Head from 'next/head';
import Header from '../components/Header';
import Profile from '../components/Profile';
import Footer from '../components/Footer';
import Data from '../data/data.json';
  • Next.jsからHeadをインポート
  • HeaderProfileFooterコンポーネントをインポート
  • data.jsonからテキストをインポート

HeaderProfileFooterコンポーネントはNextスターターキットと変更なし

※参考:【React】Nextスターターキットを作った-2. コンポーネント編(Next + TypeScript + CSS in JS) - クモのようにコツコツと


テキスト設定

const headerTitle = Data.header.title;
const pageTitle = 'このアプリについて';
const pageText = '文字の行長、行間、ジャンプ率を動的に変更した画面を確認することができます。';
const headTitle = pageTitle + ' | ' + headerTitle;
  • headerTitleDataから取得
  • pageTitlepageTextはベタガキ
  • headerTextpageTitleheaderTitleの組み合わせ

先ほどのトップページと同じ


Aboutコンポーネント設定

// Component
function About() {
    return (
        <>
        <Head>
            <title>{ headTitle }</title>
            <meta name="description" content={ pageText } />
            <meta property="og:title" content={ headTitle } />
            <meta property="og:description" content={ pageText } />
        </Head>

        <Header />
        <main>
            <h1>{ pageTitle }</h1>
            <p dangerouslySetInnerHTML={{ __html: pageText }}></p>
            <section>
                <h2>初期値</h2>
                <p>下記を推奨スタイルとして初期値にしています。</p>
                <ul>
                    <li>行長:35文字( max-width: 35em; )</li>
                    <li>行間:1.75倍( line-height: 1.75em; )</li>
                    <li>ジャンプ率:200%( font-size: 2em; )</li>
                </ul>
                <p>※参考:<a href="https://www.i-ryo.com/entry/2019/02/19/230354">【行長・行間・ジャンプ率】タイポグラフィ事始め(適度な箱組みとは)</a></p>
            </section>
            <Profile />
        </main>
        <Footer />
        </>
    );
}

トップページと同じくOGP設定はabout.tsxに直に設定。また、sectionタグの部分は本ページ固有の内容のためベタガキ。


最後にAboutコンポーネントをエクスポート

export default About;

Dev-toolsの「Sources」を見ると静的なOGP設定になっていることがわかる! f:id:idr_zz:20210504121520j:plain

※参考:このアプリについて | ジャンプ率ジェネレーター

Innerコンポーネント(style属性で設定)

文字設定を変更するジェネレーター本体はInnerコンポーネントに書いている。

※参考:jump-rate-generator-2/Inner.tsx at main · ryo-i/jump-rate-generator-2 · GitHub


React版の時は関数コンポーネントの中でStyled-coponentsで設定していた

const P = styled.p`
     line-height: ${lineHeight}em;
   `;

   const Section = styled.section`
     max-width: ${lineLength}em;
   `;

   const H2 = styled.h2`
     && {
       font-size: ${jumpRate}em;
     }
   `;

上記のコンポーネントをJSXでレンダリングしていた

      <Example>
        <Section>
          <H2>タイトルです、ああタイトルです、タイトルです</H2>
          <P>本文です。本文です。本文ですったら、本文です。本文です。本文です。本文ですったら、本文です。本文です。本文です。本文ですったら、本文です。本文です。本文です。本文ですったら、本文です。本文です。本文です。本文ですったら、本文です。本文です。本文です。本文ですったら、本文です。本文です。本文です。本文ですったら、本文です。本文です。本文です。本文ですったら、本文です。本文です。本文です。本文ですったら、本文です。</P>
        </Section>
      </Example>

※参考:【React】styled-componentsにフックの値を設定し、動的にスタイルを変更(ジャンプ率ジェネレーター完成!) - クモのようにコツコツと


しかし、上記のままだとNext版では変更が反映されなかった。

React版はSPAのためJSXのHTMLがリアルタイムにレンダリングされる。CSS設定が変更されるとStyled-componentsがタグに振っているclass名も動的に変更される。

しかしNext版の場合はStyled-componentsが振っているclass名が固定されたままなのでCSSの設定も変わらない。Next.jsのHTMLタグはSPAに比べて静的なHTMLということだ。

Next.jsでCSSを動的に変更する方法に調べ、いろいろ試してみたが自分の環境ではうまくいかなかった。。

※参考:Next.jsでstyled-componentsを使うときに最初に設定しておくこと
※参考:Advanced Features: カスタム `Document` | Next.js
※参考:Next.jsにstyled-componentsを取り込む方法【Babelの設定が必要】
※参考:GitHub - styled-components/babel-plugin-styled-components: Improve the debugging experience and add server-side rendering support to styled-components
※参考:styled-components: Advanced Usage
※参考:next.js/_document.js at master · vercel/next.js · GitHub

そもそも自分がやりたいことが違うのかもしれない。styled-componentsの動的変更について調べるとたいていはpropsと分岐条件で値を変更する内容。

※参考:styled-components: Basics

styled-componentsのclass名がリアルタイムに都度代わる方法はNextとは相性が悪いように感じる。


Reactドキュメントにはインラインスタイル(style属性を直接変更)についてこうあった。

style 属性を要素のスタイリングの主要な手段として使うことは一般的に推奨されません。多くの場合、className を使って外部の CSS スタイルシートに定義された CSS クラスを参照するべきです。React アプリケーションの中では、style は動的に計算されたスタイルをレンダー中に追加するために最もよく使われます

※参考:DOM 要素 – React

「動的に計算されたスタイルをレンダー中に追加する」は今回の状況に合っているのではないか。それ以外の基本的なスタイル設定はstyle-componentsを使ってclassNameで設定できているわけで。


先ほどstyled-componentsで設定していた部分をただの連想配列にする

  const pStyle = {
    lineHeight: lineHeight + 'em'
  };

  const sectionStyle = {
    maxWidth: lineLength + 'em'
  };

  const h2Style = {
    fontSize: jumpRate + 'em'
  };

キー名はハイフン(line-heightなど)ではなくキャメルケース(lineHeightなど)

※参考:DOM 要素 – React

section、h2、pはただのタグに戻して、style属性で先ほどの設定を読み込む。

      <Example>
        <section style={sectionStyle}>
          <h2 style={h2Style}>タイトルです、ああタイトルです、タイトルです</h2>
          <p style={pStyle}>本文です。本文です。本文ですったら、本文です。本文です。本文です。本文ですったら、本文です。本文です。本文です。本文ですったら、本文です。本文です。本文です。本文ですったら、本文です。本文です。本文です。本文ですったら、本文です。本文です。本文です。本文ですったら、本文です。本文です。本文です。本文ですったら、本文です。本文です。本文です。本文ですったら、本文です。本文です。本文です。本文ですったら、本文です。</p>
        </section>
      </Example>
  • sectionタグのstyle属性にsectionStyleを設定
  • h2タグのstyle属性にh2Styleを設定
  • pタグのstyle属性にpStyleを設定

※参考:jump-rate-generator-2/Inner.tsx at main · ryo-i/jump-rate-generator-2 · GitHub


これでツマミをいじるとCSSが動的に変更されるようになった!
f:id:idr_zz:20210504120003j:plain

Dev-toolsの「Elements」を見るとタグにstyle属性が追加されているのがわかる。
f:id:idr_zz:20210504120007j:plain
styles-componentsのclass名は変更がないまま。

※参考:ジャンプ率ジェネレーター

最後に

ということでジャンプ率ジェネレータをNext版に作り直しました。

ReactのSPAとNext.jsの静的HTMLによってCSS in JSの挙動に違いがあるのがちょっと戸惑いましたが、全体的にはOGPやルーティング以外のコアな部分のコンポーネントはほとんど変更がないままスムーズに作り直すことができました。ページごとのOGPがちゃんと表示さっるのも嬉しいです♪

こんな感じで今後もしばらくはNext環境でWebアプリを作っていきたく思います。それではまた!


※参考:ReactでWebアプリを作るシリーズまとめ
qiita.com