Управление состояниями в Gatsby
Development | Комментировать запись
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: Gatsby