Управление состояниями в 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