クモのようにコツコツと

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

【React】react-helmetでheadタグの中身を動的に打ち替える(Reactとメタ言語の比較-2)

Reactの続きです。前回は以前作成したメタ言語スターターキットの内容を移植してHTML部分をコンポーネント化しました。今回はheadタグを動的に打ち替えたく、react-helmetを使ってみます。それではいきましょう!

【目次】

※参考:前回記事
【React】HTMLコンポーネント化(Reactとメタ言語の比較-1) - クモのようにコツコツと

※参考:Reactを習得するためにやったことまとめ
qiita.com

前回のおさらい

以前作成したメタ言語スターターキットのコンパイル語のコードを移植。

https://cdn-ak.f.st-hatena.com/images/fotolife/i/idr_zz/20210202/20210202065525.jpg

React + TypeScript + CSS in JSの構造で作り替えて比較したい。まずはHTML部分をReactのコンポーネント化した。

※参考:【React】HTMLコンポーネント化(Reactとメタ言語の比較-1) - クモのようにコツコツと

前回はheadタグにベタ書き

前回、「src」フォルダの「index.tsx」でHTMLをコンポーネント化した

ReactDOM.render(
  <React.StrictMode>
    <Header />
    <Main />
    <Footer />
  </React.StrictMode>,
  document.getElementById('root')
);

HeaderMainFooterコンポーネントを読み込み#rootにレンダリングする。

※参考:react-from-meta-lang/index.tsx at 8478499fbdd193e07008264f5bd2df0b5f2d7354 · ryo-i/react-from-meta-lang · GitHub


#rootは「public」フォルダの「index.html」にある。

<body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <!-- 中略 -->
</body>

headタグの中にはtitledescriptionのテキストがベタ書きになっている。

<head>
    <!-- 中略  -->
    <meta
      name="description"
      content="以前作ったメタ言語スターターキットの内容をReact環境で再現してみる"
    />
    <!-- 中略  -->
    <title>Reactとメタ言語の比較</title>
  </head>

※参考:react-from-meta-lang/index.html at 8478499fbdd193e07008264f5bd2df0b5f2d7354 · ryo-i/react-from-meta-lang · GitHub


メタ言語スターターキットのEJSの時はheadタグの中身も外部から読み込んだテキストで打ち替えていた。

<meta charset="UTF-8">
    <title><%= header.title %></title>
    <meta name="description" content="<%= header.text %>">
    <link rel="stylesheet" href="css/style.css">
    <script src="js/script.js"></script>

※参考:【メタ言語】フロントエンド開発スターターキットを作った(EJS、Sass(SCSS)、TypeScript) - クモのようにコツコツと

こういうことをReactでもできないものか。Reactはbodyタグ内のHTMLタグにJSXをレンダリングするからなー。

そうだ、react-helmetでheadタグ 打ち替えよう

調べるとReactでheadタグを打ち変える「react-helmet」というパッケージがあることを知った。

※参考:react-helmet - npm

その他、参考記事

※参考:Gatsby.jsにreact-helmetを導入してhead要素(メタタグ)をカスタマイズする | webOpixel
※参考:metaタグを変えるためにreact-helmetを導入してみたけれど結局ES2015で実装した話 - LCL Engineers' Blog
※参考:SSR無しでReactアプリをOGPとかに対応させる (自力prerendering編) - Qiita

よし、いっちょやってみっか〜(悟空の声で)

App.tsxを作成

headがある「index.html」自体ではReactは動かせない。ここにはJSXをレンダリングする起点となる#rootがあるだけだ。

こちらの記事は自分と同じく 「Create React App」でreact-helmetを導入している事例がある!

※参考:SSR無しでReactアプリをOGPとかに対応させる (自力prerendering編) - Qiita

「Create React App」の「App.js」でhelmetを設定し、「index.html」のheadタグを打ち替えできているようだった。


前回、「App.tsx」をいったん削除していたが再度作成する。 f:id:idr_zz:20210204065515j:plain


「index.tsx」にはHeaderMainFooterコンポーネントをインポートしていたが、Appコンポーネントだけをインポートする。

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';


ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

「index.tsx」でインポートしていたHeaderMainFooterコンポーネントはこの「App.tsx」で読み込むことにする。

import React from 'react';
import Header from './Header';
import Main from './Main';
import Footer from './Footer';

function App() {
  return (
    <div className="App">
      <Header />
      <Main />
      <Footer />
    </div>
  );
}

export default App;

よし、ここにreact-helmetを導入するどー♪

react-helmetのインストール

react-helmetをインストールする。こちらのコマンド

npm i react-helmet

※参考:react-helmet - npm


TypeScript環境なので以前インストールした「styled-components」と同様、今回も@typesも入れないとエラーになった。

※参考:【React】React + TypeScript + CSS in JSの開発環境を作る(Gitエラー対処も) - クモのようにコツコツと

こちらのコマンド

npm i @types/react-helmet --save-dev

@typesのコマンドはこちらで調べることができる。

※参考:TypeScript: Search for typed packages


package.jsonを確認。react-helmetdependencies

  "dependencies": {
    // 中略
    "react-helmet": "^6.1.0",
    // 中略
  },

@types/react-helmetdevDependenciesに追加された

  "devDependencies": {
    "@types/react-helmet": "^6.1.0"
  }

Helmetコンポーネントを設定

公式ドキュメントを参考にHelmetコンポーネントを設定する。

※参考:react-helmet - npm

このようにHelmetコンポーネントの中にベタ書き。

function App() {
  return (
    <div className="App">
        <Helmet>
            <meta charSet="utf-8" />
            <title>My Title</title>
            <link rel="canonical" href="http://mysite.com/example" />
        </Helmet>
        <Header />
        <Main />
        <Footer />
    </div>
  );
}

アプリを起動すると

$ npm start

ローカル環境でアプリが表示される https://cdn-ak.f.st-hatena.com/images/fotolife/i/idr_zz/20210202/20210202065525.jpg

Dev-toolsで確認すると、おお!titleが「My Title」に変わっている! f:id:idr_zz:20210204065837j:plain

変数のテキストでtitleとdescriptionを打ち替え

headタグをReactで打ち替えられたのは嬉しいが、このままだとテキストがベタ書きなのは変わらない。やはり動的に打ち替えたい。

こちらの記事のようにHelmetコンポーネントの中に属性として設定してみよう。

※参考:Gatsby.jsにreact-helmetを導入してhead要素(メタタグ)をカスタマイズする | webOpixel

まず、変数titleTextdescriptionTextにテキストを設定

const titleText = 'Reactとメタ言語の比較';
const descriptionText = '以前作ったメタ言語スターターキットの内容をReact環境で再現してみる';

titleTextdescriptionTextHelmetコンポーネントの属性に設定する。

function App() {
  return (
    <div className="App">
        <Helmet
            title={ titleText }
            meta={[
                { name: 'description', content: descriptionText }
            ]}
         />
        <Header />
        <Main />
        <Footer />
    </div>
  );
}

属性の値部分は{}で囲って、さらにmetaタグは複数設定できるので[]で配列にする。


「index.html」のtitleタグは全然違うテキストにしてみた。

  <head>
    <!-- 中略 -->
    <title>helmetで打ち替えテストだよ〜ん</title>
  </head>

なお、descriptionは上書きされず二重になったため、index.htmlの方はは消してみた。下記の記事と同じ挙動。

※参考:metaタグを変えるためにreact-helmetを導入してみたけれど結局ES2015で実装した話 - LCL Engineers' Blog


ブラウザを確認すると、おお、titleとdescriptionが打ち変わった! f:id:idr_zz:20210204071212j:plain

Sorcesパネルを見るとtitleは「helmetで打ち替えテストだよ〜ん」のまま。 f:id:idr_zz:20210204071311j:plain


ソース(GitHub)※今回のコミットまで

※参考:GitHub - ryo-i/react-from-meta-lang at b5806001337be58355f4843de94666397ab52857

プレビュー(GitHub Pages)

※参考:Reactとメタ言語の比較

最後に

ということで、Reactでheadタグの中身を打ち変えることができました。これでページtitleとh1タグを共通テキストから読み込んで、メンテナンスをあげることができそうです。

今回は取り急ぎ変数のテキストを読み込んだけど、外部のJSONファイルから読み込む構成にしていきたいと思います。

また、headタグをReactで打ち替えられるということで、「react-router(ページのURLパスを変更)」と組み合わせて、SPA(シングルページアプリケーション)のページ遷移が表現することができそう♪

※参考:SSR無しでReactアプリをOGPとかに対応させる (自力prerendering編) - Qiita それではまた!


※参考:Reactを習得するためにやったことまとめ
qiita.com