Управление состояниями в Gatsby

Gatsby обрабатывает маршруты за нас, а потому мы не можем обернуть приложение в стор или провайдер Redux. В этой статье мы покажем один хитрый трюк, который позволяет обойти это ограничение.

Читайте также:

Для простоты во всех примерах мы будем использовать Context API – он упростит процесс управления состоянием и сэкономит время на создании шаблона (но при этом все нижеперечисленные инструкции применимы и к другим методам управления состоянием).

Установка node-sass

Начать можно с очень простой темы, и в результате нам достаточно получить две страницы, которые подтвердят, что состояние применяется глобально. Поскольку мы будем работать со стилями, давайте добавим поддержку Sass с помощью пакетов node-sass и gatsby-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>
);

Теперь у нас есть способ управлять состоянием. Gatsby предлагает небольшой хук под названием wrapRootElement, о котором вы можете почитать в документации. Этот хук берет большую часть сайта и передает его в качестве свойств в функцию, которую мы ему предоставляем – например, в ту, которую мы только что экспортировали из 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 и получить доступ к глобальному состоянию в контексте. 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, вы сможете изменять состояние и перемещаться между ними, при этом состояние останется согласованным и последовательным.

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>
);

Попробуйте открыть свое приложение в браузере. На экране вы увидите простую страницу с белым фоном, заголовком Light Theme и кнопкой Dark. Если вы нажмете на эту кнопку, фон станет темным, заголовок изменится на Dark Theme, а кнопка – на Light.

Как видите, все это довольно просто: достаточно внести три небольших изменения и завернуть все в consumer.

Заключение

Возможно, это и не самый очевидный способ управления состоянием при всей его простоте. Надеемся, что эта статья помогла вам понять, как использовать хук wrapRootElement в интересах вашего приложения.

Репозиторий с кодом вы можете найти здесь.

Tags:

Добавить комментарий