【Gatsby.js, React】ページ遷移させたいときは navigate() を使う

こんにちは、ndj です。

現在、勉強がてら `Gatsby.js` を使用して簡単なホームページを作成しています。
ヘッダーに言語を選択できる `select` ボックスを作成して、選択した言語に合わせてページを遷移したいなと思ったのですが、ハマってしまったので備忘録として残しておきます。

(2021/03/01) 追記：navigate を使用する場合は、Router を使用しなくてもよいことがわかったので、「this.props.history.push ではなく、navigate() を使用する」のコード部分を修正。

## 使用技術とバージョン情報

- gatsby: 2.32.3
- gatsby-source-microcms: 1.0.1
- react-router-dom: 5.2.0
- react: 16.12.0

## 結論

- `React` 標準で使用される、`this.props.history.push` ではなく、`gatsby` の `navigate()` を使用する。

## やりたかったこと

amazon など、グローバルに使用されているサービスってヘッダーに言語情報を選択できるようになっていますよね。

![スクリーンショット 2021-02-23 231313.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/485313/950e54ca-a894-7005-8d37-bfbe0fa48dd9.png)

amazon みたいに、言語情報を選択したら、その言語のページに遷移できるようにしたいなと。

## 試みたこと

当初は、以下のように、 `<select>`タグ の `onChange` イベントハンドラで `this.props.history.push(e.target.value)` をして、ページを遷移しようとしていました。
参考：[codesandbox.io: w031p82nr5](https://codesandbox.io/s/w031p82nr5?file=/src/index.js)

また、**言語別の説明文は headlessCMS に保存しており、ページ生成の際に、`gatsby-node.js` の `createPage` で `<Home />` に context を渡す**。みたいな感じでページを生成しておりました。

```jsx:header.js
import React from 'react';
import { useStaticQuery, Link, navigate } from 'gatsby';
import { Switch, Route, BrowserRouter as Router, withRouter } from 'react-router-dom';
import Home from '../pages/locale';

class DropDown extends React.Component {
    onChange = (e) => {
        this.props.history.push(`/${e.target.value}`);
    }
    render() {
        return (
          <select onChange={this.onChange}>
            <option value="ja">日本語</option>
            <option value="en">English</option>
          </select>
        );
    }
}

const Menu = withRouter(DropDown);

export default function Header() {
    return (
        <Router>
            <header className="header">
                <nav>
                    <ul id="header-list">
                        <li>
                            <Menu />
                        </li>
                        <li>
                            <Link to="/">Install now</Link>
                        </li>
                    </ul>
                </nav>

            </header>

            <Switch>
                <Route path='/ja' render={() => <Home locale="ja" />} />
                <Route path='/en' render={() => <Home locale="en" />} />
            </Switch>
        </Router>

    );
}
```

こんな感じでページ遷移を実現させたかったんですが、エラーが出る。。。

```
Uncaught TypeError: Cannot read property 'post' of undefined
```

## 原因っぽい部分

エラー自体の原因は、以下の部分です。

```error.jsx
<Route path='/ja' render={() => <Home locale="ja" />} />
```

先述のように、言語別の説明文は headlessCMS に保存しており、ページ生成の際に、`gatsby-node.js` の `createPage` で `GraphQL query` を投げて、 `<Home />` に context を渡す。みたいな感じでページを生成しておりました。
つまり、上記のコードだと `select` ボックスの選択肢が変化したときに context を渡せないので、エラーが出てたという感じです。

なので、**ページ遷移の方法を工夫する必要がありそう**です。

## this.props.history.push ではなく、navigate() を使用する

`DropDown` の `onChange` の部分を以下のように変更します。
`gatsby` に専用の function があるみたいです。
参考：[gatsbyjs：How to use the navigate helper function](https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-link/#how-to-use-the-navigate-helper-function)
参考：[stackoverflow: What's the equivalent of this.props.history.push in gatsby?](https://stackoverflow.com/questions/58677568/whats-the-equivalent-of-this-props-history-push-in-gatsby)

```jsx:header.js
import { Link, navigate } from 'gatsby';

class DropDown extends React.Component {
    onChange = (e) => {
        navigate(`/${e.target.value}`);
    }
    render() {
        return (
          <select onChange={this.onChange}>
            <option value="ja">日本語</option>
            <option value="en">English</option>
          </select>
        );
    }
}

export default function Header() {
    return (
        <React.Fragment>
            <header className="header">
                <nav>
                    <ul id="header-list">
                        <li>
                            <DropDown />
                        </li>
                        <li>
                            <Link to="/">Install now</Link>
                        </li>
                    </ul>
                </nav>
            </header>
        </React.Fragment>

    );
}
```

## 最後に
`gatsby` もさることながら、 `React` 自体の習熟度も低く解決に時間がかかってしまいましたが、コツコツやっていきたいと思います。
また、今回解決方法を検索する際に積極的に英語のページを見るようにしましたが、翻訳機能様の存在とソースは全世界で共通なので頑張れば意外といけました。

誤字脱字、技術的なご指摘などございましたら、コメントいただけますとありがたいです。
ここまで読んでいただきありがとうございました。
これからも巨人の肩に乗り続けます。

## 参考
[amazon](https://www.amazon.co.jp/)
[codesandbox.io: w031p82nr5](https://codesandbox.io/s/w031p82nr5?file=/src/index.js)
[gatsbyjs：How to use the navigate helper function](https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-link/#how-to-use-the-navigate-helper-function)
[stackoverflow: What's the equivalent of this.props.history.push in gatsby?](https://stackoverflow.com/questions/58677568/whats-the-equivalent-of-this-props-history-push-in-gatsby)