WrapRootElementフックを使用したGatsbyの状態管理
Gatsbyがルートを処理するため、Reduxストアまたはプロバイダーでアプリをラップする場所がありません。 この記事では、それを回避するための巧妙なトリックを学びます。
簡単にするために、すべての例では、状態管理にReactの Context API を使用して、ボイラープレートの設定にかかる時間を節約していますが、他の状態管理方法にもすべて適用できます。 プロバイダーとの連携をブラッシュアップする必要がある場合は、useContextフックのこのイントロを確認できます。
インストール
私は非常に基本的なテーマから始めることを好みますが、最終的には2ページの何かが必要なだけなので、私たちの状態がグローバルに適用されていることがわかります。 いくつかのスタイリングで作業するので、私は動物ではないので、node-sass
とgatsby-plugin-sass
でSassサポートを追加します。
$ gatsby new stateful-gatsby https://github.com/gatsbyjs/gatsby-starter-defaultCopyInstall $ cd stateful-gatsby $ yarn add node-sass gatsby-plugin-sass -D
プロバイダーによるラッピング
最初のステップは、単純なisDark
状態と、現在の状態を逆にするメソッドを使用してコンテキストプロバイダーをセットアップすることです。 渡されたものはすべて小道具として受け取り、新しいmyContext.Provider
でラップします。
Provider.js
import React, { useState } from 'react'; export const myContext = React.createContext(); const Provider = props => { const [isDark, setTheme] = useState(false); return ( <myContext.Provider value={{ isDark, changeTheme: () => setTheme(!isDark) }}> {props.children} </myContext.Provider> ) };
そのすぐ下に、新しいプロバイダーで渡されたものをすべてラップする関数をエクスポートします。
export default ({ element }) => ( <Provider> {element} </Provider> );
状態を管理する方法ができたので、GatsbyはwrapRootElement
と呼ばれる小さなフックを提供します。これは、docsで確認できます。 このフックは、サイトの大部分を取得し、Provider.js
からエクスポートした関数のように、プロップとして提供する関数に渡します。これにより、すべてを内部にラップするのに最適な小さなスペースが得られます。
gatsby-browser.js
とgatsby-ssr.js
の両方がこのフックにアクセスでき、両方をプロバイダーでラップすることをお勧めします。そのため、provider.js
でラッパー関数を定義しました。
import Provider from './provider'; export const wrapRootElement = Provider;
スタイリング
シンプルなテーマスタイルは次のとおりです。
src / global.sass
.colorTheme height: 100vh transition: .3s ease-in-out .darkTheme @extend .colorTheme background-color: #1A202C color: #fff a color: yellow .lightTheme @extend .colorTheme background-color: #fff color: #000
テーマの適用
状態にアクセスするために必要なのは、各コンポーネントをmyContext.Consumer
でラップし、context
、React.Fragmentのグローバル状態にアクセスすることだけです。複数の要素を追加します。
背景色については、条件付きでクラスを状態に設定できます。複数のテーマがある場合は、プロバイダーのテーマをクラス名の文字列として設定できます。
layout.js
import { myContext } from '../../provider'; import '../global.sass'; return ( <myContext.Consumer> {context => ( <React.Fragment> <div className={context.isDark ? 'darkTheme' : 'lightTheme'}> {/* ... */} </div> </React.Fragment> )} </myContext.Consumer> )
changeTheme
メソッドに渡したため、setTheme
にもアクセスできます。 isDark
を反転するボタンを追加しましょう。
src / pages / index.js
import { myContext } from '../../provider'; const IndexPage = () => ( <Layout> <myContext.Consumer> {context => ( <React.Fragment> <SEO title="Home" /> <h1>{context.isDark ? "Dark Theme" : "Light Theme"}</h1> <button onClick={() => context.changeTheme()}>{context.isDark ? "Light" : "Dark"}</button> <Link to="/page-2/">Go to page 2</Link> </React.Fragment> )} </myContext.Consumer> </Layout> );
これで、page-2.js
に基本的に同じ構成を追加すると、状態を変更してそれらの間を移動できるようになり、状態はそれらの間で一貫したままになります。
page-2.js
import { myContext } from '../../provider'; const SecondPage = () => ( <Layout> <myContext.Consumer> {context => ( <React.Fragment> <SEO title="Page two" /> <h1>{context.isDark ? "Dark Theme" : "Light Theme"}</h1> <p>Welcome to page 2</p> <button onClick={() => context.changeTheme()}>{context.isDark ? "Light" : "Dark"}</button> <Link to="/">Go back to the homepage</Link> </React.Fragment> )} </myContext.Consumer> </Layout> );
フザ! それは本当にそれと同じくらい簡単です。3つの小さなファイルの変更とすべてをコンシューマーでラップするので、準備は完了です。
結論
私にとって、これは物事を行うための非常に明白な方法ではなかったので、これがwrapRootElement
フックを有利に使用する方法を理解するのに役立つことを願っています。
便宜上、この設定をスターターとして使用してリポジトリを作成しました。これは、ここでチェックアウトできます。