開発者ドキュメント

wrapRootElementフックを使用したGatsbyの状態管理

Gatsbyがルートを処理するため、Reduxストアまたはプロバイダーでアプリをラップする場所がありません。 この記事では、それを回避するための巧妙なトリックを学びます。

簡単にするために、すべての例では、状態管理にReactの Context API を使用して、ボイラープレートの設定にかかる時間を節約していますが、他の状態管理方法にもすべて適用できます。 プロバイダーとの連携をブラッシュアップする必要がある場合は、useContextフックこのイントロを確認できます。

インストール

私は非常に基本的なテーマから始めることを好みますが、最終的には2ページの何かが必要なだけなので、私たちの状態がグローバルに適用されていることがわかります。 いくつかのスタイリングで作業するので、Sassサポートを追加します node-sassgatsby-plugin-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>
);

状態を管理する方法ができたので、ギャツビーは私たちにと呼ばれるきちんとした小さなフックを提供します wrapRootElementdocsで確認できます。 このフックは、サイトの大部分を取得し、エクスポート元の関数のように、指定した関数に小道具として渡します。 Provider.js、すべてを中に包むのに最適な小さなスペースを提供してくれます。

両方 gatsby-browser.jsgatsby-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>
  )

また、にアクセスできます setTheme に渡したので changeTheme 方法。 反転するボタンを追加しましょう 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 あなたの利点にフックします。

便宜上、この設定をスターターとして使用してリポジトリを作成しました。これは、ここでチェックアウトできます

モバイルバージョンを終了