Используйте React Context API с Gatsby

Я немного опоздал на вечеринку, используя новый React Context API, я использовал его на днях на работе, я также сделал фрагмент, чтобы создать для него компонент.

Я придерживался нескольких руководств, которые объясняли, как им пользоваться, и ни одно из них не так хорошо, как это замечательное объяснение того, как им пользоваться от @leighchalliday, спасибо, Ли? Это отличный вариант использования, который помог мне понять, как им пользоваться.

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

Следует иметь в виду, что это для моего конкретного случая использования, поэтому я заранее извиняюсь, если что-то из этого вводит в заблуждение, я пытаюсь задокументировать это, чтобы помочь мне также это понять?

С Gatsby, если вы хотите использовать React 16.3, вам нужно будет установить следующий плагин Gatsby React:

npm install gatsby-plugin-react-next

Gatsby использует React 16.2, я считаю, поэтому вам нужно будет использовать этот плагин.

Другое, что вам может понадобиться сделать, это:

npm i react react-dom
npm un react react-dom

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

Еще одна вещь, которую вы можете рассмотреть, если оказывается, что ничего не работает, попробуйте воспользоваться npm ci команда. Это версия npm 6+ для удаления вашего node_modules папку и переустановку. ?

Давайте рассмотрим один из моих любимых случаев использования на данный момент и добавим поддержку тем на сайт Gatsby и используем контекстный API React для управления темой.

Вы можете увидеть, как тема приложения React без React Context API в моих styled-components? Начало работы.

Для иллюстрации я расскажу это здесь сейчас, вы добавьте ThemeProvider на самом высоком уровне в структуре программы, чтобы все потомки/дети программы имели доступ к ней.

Я уже сделал это для своего личного сайта, и теперь я собираюсь сделать это здесь, и давайте рассмотрим это вместе.

Сделаем компонент!

Ладно, значит, все в React является компонентом, поэтому мне это так нравится — давайте создадим a SomethingContext.js компонент, как я хочу делать вещи из styled-components ?

Давайте начнем с того, что дадим ему образное название:

touch src/layouts/components/BlogThemeContext.js

Там мы идем?

Ладно, «вещи», которые я хочу сделать с контекстным API:

  1. изменить стилевые компоненты ThemeProvider
  2. вращать шаблоны героя сайта

Теперь, чтобы расширить контекстный компонент, я уже упоминал фрагмент кода VS для личного использования, являющегося основной структурой для Context состоящий из двух частей, а Provider и а Consumer

Давайте создадим Context и Consumer в этом компоненте.

Используя фрагмент, он должен выглядеть примерно так:

src/layouts/components/BlogThemeContext.js
import React from 'react';
// Context is made up of two things
// Provider - Single as close to top level as possible
// Consumer - Multiple have multiple consumers
export const BlogThemeContext = React.createContext();

export class BlogThemeProvider extends React.Component {
  state = {
    item1: 1,
    item2: 2,
  };

  // add function here
  functionHere = () => {
    this.setState({
      item1: 2,
      item2: 3,
    });
  };
  render() {
    return (
      <BlogThemeContext.Provider
        value={{
          ...this.state,
          functionHere: this.functionHere,
        }}>
        {this.props.children}
      </BlogThemeContext.Provider>
    );
  }
}

Итак, props перешел в <BlogThemeContext.Provider> это состояние и методы, содержащиеся в BlogThemeProvider к ним можно получить доступ во всей программе с помощью <BlogThemeContext.Consumer>.

Теперь давайте добавим BlogThemeProvider на верхнем уровне нашего приложения, чтобы состояние и функции поставщика были доступны для детей layout/index.js.

Вот как это выглядит перед добавлением поставщика контекста, вы заметите, что styled-components ThemeProvider здесь есть компонент верхнего уровня.

src/layouts/index.js
const TemplateWrapper = ({ children }) => (
  <ThemeProvider theme={theme}>
    <PageContainer>
      <Helmet title={nameContent} meta={siteMeta} />
      <Header />
      <Main>{children()}</Main>
      <Footer />
    </PageContainer>
  </ThemeProvider>
);

Теперь у нас уже есть styled-компоненты ThemeProvider который получает a theme объекта, и мы хотим управлять темой в нашем поставщике контекста. Итак, давайте импортируем существующую тему из globalStyle модуль в BlogThemeContext и добавить theme до состояния BlogThemeProvider:

import React from 'react';
import PropTypes from 'prop-types';

import { theme } from '../../theme/globalStyle';

// Context is made up of two things
// Provider - Single as close to top level as possible
// Consumer - Multiple have multiple consumers
export const BlogThemeContext = React.createContext();

export class BlogThemeProvider extends React.Component {
  state = {
    theme,
  };

  // add function here
  functionHere = () => {
    this.setState({
      item1: 2,
      item2: 3,
    });
  };
  render() {
    return (
      <BlogThemeContext.Provider
        value={{
          ...this.state,
          functionHere: this.functionHere,
        }}>
        {this.props.children}
      </BlogThemeContext.Provider>
    );
  }
}

BlogThemeProvider.propTypes = {
  children: PropTypes.any,
};

Пока мы здесь, давайте также добавим функцию для обработки смены темы путем замены манекена functionHere функционировать в фрагменте, а также добавлять темы, между которыми мы хотим переключаться.

import React from 'react';
import PropTypes from 'prop-types';

import { theme1, theme2 } from '../../theme/globalStyle';

export const BlogThemeContext = React.createContext();

export class BlogThemeProvider extends React.Component {
  state = {
    theme,
  };

  handleThemeChange = e => {
    let theme = e.target.value;
    theme === 'theme1' ? (theme = theme1) : (theme = theme2);
    this.setState({ theme });
  };
  render() {
    return (
      <BlogThemeContext.Provider
        value={{
          ...this.state,
          handleThemeChange: this.handleThemeChange,
        }}>
        {this.props.children}
      </BlogThemeContext.Provider>
    );
  }
}

BlogThemeProvider.propTypes = {
  children: PropTypes.any,
};

Использовать Context.Consumer

Итак, давайте воспользуемся этим, не правда ли? Способ использования очень похож на styled-component ThemeProviderимпортируйте ваш <ThemeSelectProvider> тогда вы можете использовать <ThemeSelectContext.Consumer> для доступа к функциям и состоянию BlogThemeContext через <ThemeSelectProvider>

Дочерний элемент потребителя является функцией, поэтому вместо того, чтобы вернуть вашу программу, как это было бы с обычным компонентом React, так:

<Wrapper>
  <Child />
</Wrapper>

Итак, вам нужно встроить следующую функцию:

<Wrapper>{() => <Child />}</Wrapper>

Итак, вы возвращаете (в этом примере, <Child />) приложение как результат <Context.Consumer> функцию, здесь мы также можем получить какие-либо свойства или состояние из контекста, в моем случае использование здесь я хочу получить theme prop из поставщика контекста value (<BlogThemeProvider>), поэтому мы воспользуемся деструктуризацией ES6, чтобы вытащить theme объект.

Стилизованные компоненты ThemeProvider теперь можно использовать theme объект, предоставленный <BlogThemeContext.Consumer> поэтому можно безопасно удалить импорт из globalStyle.

const TemplateWrapper = ({ children }) => (
  <BlogThemeProvider>
    <BlogThemeContext.Consumer>
      {({ theme }) => (
        <ThemeProvider theme={theme}>
          <PageContainer>
            <Helmet title={nameContent} meta={siteMeta} />
            <Header />
            <Main>{children()}</Main>
            <Footer />
          </PageContainer>
        </ThemeProvider>
      )}
    </BlogThemeContext.Consumer>
  </BlogThemeProvider>
);

Также есть шаблон src/template/blog-posts.js которую Гетсби использует для создания сообщений в этом блоге, давайте сделаем так же, давайте обратим программу в функцию возврата для потребителя контекста, прежде чем она выглядела так:

const Template = ({ data, pathContext }) => {
  const { markdownRemark: post } = data
  const { frontmatter, html } = post
  const { title, date } = frontmatter
  const { next, prev } = pathContext

  return (
    <PostWrapper border={({ theme }) => theme.primary.light}>
      <Helmet title={`${title} - blog.scottspence.me`} />
      <Title>{title}</Title>
      <TitleDate>{date}</TitleDate>
      ....

Теперь это выглядит так:

const Template = ({ data, pathContext }) => {
  const { markdownRemark: post } = data
  const { frontmatter, html } = post
  const { title, date } = frontmatter
  const { next, prev } = pathContext

  return (
    <BlogThemeProvider>
      <BlogThemeContext.Consumer>
        {({ theme }) => (
          <PostWrapper border={({ theme }) => theme.primary.light}>
            <Helmet title={`${title} - blog.scottspence.me`} />
            <Title>{title}</Title>
            <TitleDate>{date}</TitleDate>
            ....

Добавьте компонент ThemeSelect

The ThemeSelect компонент – это компонент выбора, который я использовал несколько раз, вот источник с моего личного сайта, это то, что мы будем использовать для обработки изменения темы, он будет использовать handleThemeChange метод в BlogThemeContext поэтому нам лучше использовать потребителя Context для доступа к методу:

<BlogThemeContext.Consumer>
  {({ handleThemeChange }) => (
    <ThemeSelectWrapper>
      <ThemeSelect handleThemeChange={handleThemeChange} />
    </ThemeSelectWrapper>
  )}
</BlogThemeContext.Consumer>

Теперь, если мы посмотрим на state в инструментах разработчика React мы видим, как изменяется шрифт с изменением выбора темы, как и в styled-components? Начало работы.

r1b8qgu6lm5xjjondse7

Ладно, успех? ? Теперь перейдите к фоновому переключению/переходу.

Сменить героя (фоновые шаблоны)

Итак, сейчас, чтобы переключаться между потрясающими шаблонами героев Стива Шогера, у меня есть функция. globalStyle модуль, возвращающий случайный шаблон HERO:

export const randoHero = () => {
  const keys = Object.keys(HERO);
  return HERO[keys[(keys.length * Math.random()) << 0]];
};

Эта функция устанавливает фон на body каждая перезагрузка с помощью случайного ключа от HERO объект, сейчас я собираюсь переместить это к componentDidMount() с BlogThemeContext.Provider поэтому (пока) он выбирает случайный ключ из объекта каждые десять секунд:

export class BlogThemeProvider extends React.Component {
  state = {
    theme: theme1,
    background: HERO[0]
  }

  handleThemeChange = e => {
    let theme = e.target.value
    theme === 'theme1' ? (theme = theme1) : (theme = theme2)
    this.setState({ theme })
  }

  componentDidMount() {
    this.interval = setInterval(() => {
      const keys = Object.keys(HERO)
      const background =
        HERO[keys[(keys.length * Math.random()) << 0]]

      this.setState({ background })
    }, 10 * 1000)
  }

  render() {
  ....

Теперь нужно найти место в приложении, которое может изменить фон! Как я упоминал ранее, фон для страницы был установлен на body через styled-components injectGlobal Теперь я хочу получить доступ к background prop из контекста, поэтому я переместил это к src/layouts/index.js. У меня уже добавлен потребитель контекста для theme давайте деструктуризируем background также поддерживайте:

const TemplateWrapper = ({ children }) => (
  <BlogThemeProvider>
    <BlogThemeContext.Consumer>
      {({ theme, background }) => (
        <ThemeProvider theme={theme}>
          <PageContainer background={background}>
            <Helmet title={nameContent} meta={siteMeta} />
            <Header />
            <Main>{children()}</Main>
            <Footer />
          </PageContainer>
          ....

Теперь используйте background опора в основной обертке PageContainer

Теперь мы передаем как фоновое изображение, так и цвет, как атрибуты стилизованных компонентов.

const PageContainer = styled.div`
  background-color: ${props => props.theme.background};
  background-image: url("${props => props.background}");
  background-attachment: fixed;

Это! Мы использовали React Context API для доступа к состоянию и использования его в (двух) разных точках программы.

Спасибо, что прочли

Спасибо, что просмотрели все стены кода!

Если у вас есть отзывы, пожалуйста, свяжитесь с нами

Вы можете найти весь исходный код этого в моем репозитории для этого блога здесь: https://blog.scottspence.me

Вы можете прочесть другие подобные статьи в моем блоге.

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

Ваш адрес email не будет опубликован.