【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 ではなく、gatsbynavigate() を使用する。

やりたかったこと

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

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

試みたこと

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

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

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.jscreatePageGraphQL query を投げて、 <Home /> に context を渡す。みたいな感じでページを生成しておりました。
つまり、上記のコードだと select ボックスの選択肢が変化したときに context を渡せないので、エラーが出てたという感じです。

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

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

DropDownonChange の部分を以下のように変更します。
gatsby に専用の function があるみたいです。
参考:gatsbyjs:How to use the navigate helper function
参考:stackoverflow: What's the equivalent of this.props.history.push in gatsby?

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
codesandbox.io: w031p82nr5
gatsbyjs:How to use the navigate helper function
stackoverflow: What's the equivalent of this.props.history.push in gatsby?