【React】head の title をコンポーネントごとに変更する

## はじめに
タブのタイトル名やお気に入りに入れた際のタイトルは、head タグ内の title タグの値が反映されています。
```html
<head>
  <title>My test page</title>
</head>
```

今回はこの title を React コンポーネント内で変更する方法をまとめて行きたいと思います。
結論としては [react-helmet-async](https://github.com/staylor/react-helmet-async) というライブラリを使うことで解決します。

## 対象読者
- React の基本的な使い方がわかるエンジニア
- react-helmet-async の使い方が知りたい人

また、最終的なコードは下記リポジトリに記載しています。

https://github.com/1129-tame/react-helmet-async



## 環境構築
今回は vite 使ってみたかったので

 https://www.webcreatorbox.com/tech/vite-react

こちらをみながら環境構築をしました。
create-react-app よりだいぶ早くできてかなりいい感じですね。

### version
```zsh
$ node -v
v16.17.1
$ yarn -v
1.22.19
```

```
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-helmet-async": "^1.3.0",
"react-router-dom": "^6.4.3",
"@types/react": "^18.0.17",
"@types/react-dom": "^18.0.6",
"@vitejs/plugin-react": "^2.1.0",
"typescript": "^4.6.4",
"vite": "^3.1.0"
```

## 初期設定
必要なライブラリをインストールします
```
$ yarn add react-helmet-async
$ yarn add react-router-dom
```

### routing 設定
react-helmet-async を使用する前にまず、 
React Router を用いて link を押下すると別コンポーネントに遷移できるようにします。

```zsh
touch src/page1.tsx
```

```tsx:page1.tsx
export const Page1 = () => {
  return (
    <div>
      <h1>Page1</h1>
      <nav>
        <ul>
          <li>
            <a href={`/`}>home</a>
          </li>
          <li>
            <a href={`page1`}>page1</a>
          </li>
        </ul>
      </nav>
    </div>
  );
};
```
src 配下に page1.tsx を作成し、上記コードを記載します。

```tsx:app.tsx
import './App.css'

function App() {
  return (
    <div>
      <h1>home</h1>
      <nav>
          <ul>
            <li>
              <a href={`/`}>home</a>
            </li>
            <li>
              <a href={`page1`}>page1</a>
            </li>
          </ul>
        </nav>
    </div>
  )
}

export default App
```
app.tsx を修正しスッキリさせ、リンクを配置します。この時点ではまだ遷移しても page1 は表示されません。

React Router を使って、遷移した path によって表示する component を切り替えられるようにします。

```tsx:main.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import './index.css'
import { Page1 } from './page1';
import {  createBrowserRouter, RouterProvider } from "react-router-dom";

const router = createBrowserRouter([
  {
    path: "/",
    element: <App />,
  },
  {
    path: "/page1",
    element: <Page1 />,
  },
]);

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
  <React.StrictMode>
    <RouterProvider router={router} />
  </React.StrictMode>
)
```
createBrowserRouter の引数に path とそれによって表示される component を指定し、
<RouterProvider router={router} /> という component の props に指定することでページ遷移の行き来が可能になります。

## head の title をコンポーネントごとに変更
ここからが本題です。
```tsx:main.tsx
<React.StrictMode>
  <HelmetProvider>
    <RouterProvider router={router} />
  </HelmetProvider>
</React.StrictMode>
```
main component を HelmetProvider でラップします。


#### Helmet component を追加
```tsx
const componentName = "Home";
```
コンポーネントごとに表示する文字列を変数として定義しておきます。

```tsx
<Helmet>
  <title>{componentName}</title>
</Helmet>
```
こちらのコンポーネントを各ページに追加します。

最終的なコード
```tsx
import './App.css'
import { Helmet } from 'react-helmet-async';


function App() {
  const componentName = "Home";
  return (
    <div>
      <Helmet>
        <title>{componentName}</title>
      </Helmet>
      <h1>Home</h1>
      <nav>
          <ul>
            <li>
              <a href={`/`}>Home</a>
            </li>
            <li>
              <a href={`page1`}>Page1</a>
            </li>
          </ul>
        </nav>
    </div>
  )
}

export default App
```

```tsx
import { Helmet } from "react-helmet-async";

export const Page1 = () => {
  const componentName = "Page1";
  return (
    <div>
      <Helmet>
        <title>{componentName}</title>
      </Helmet>
      <h1>Page1</h1>
      <nav>
        <ul>
          <li>
            <a href={`/`}>Home</a>
          </li>
          <li>
            <a href={`page1`}>Page1</a>
          </li>
        </ul>
      </nav>
    </div>
  );
};
```

このようにするとページ遷移するとそのページのタイトルを component 名にすることができます。

## 終わりに
この技術を活用すれば、例えば詳細画面のタイトルを選択したオブジェクト名にするなどいったことができるようになると思います。

コンポーネントを追加するだけでサクッとできるので便利ですね。

## 参考文献

https://github.com/staylor/react-helmet-async

https://reactrouter.com/en/main/start/tutorial