Шаблон проектирования компонентов React — поставщик-потребитель

React.js

Шаблоны проектирования компонентов React — объединение компонентов

Шаблоны проектирования компонентов React — Render-props

Все мы знаем, что очень сложно и громоздко передавать данные между компонентами на основе пропсов, а компонентам среднего уровня нужно добавлять какие-то бесполезные пропсы для передачи данных. Сам React уже предоставил контекстный API для решения этой проблемы, но до 16.3.0 официальная рекомендация не использовать его, думая, что от него рано или поздно откажутся. Ведь многие библиотеки приняли API контекста. Ты видишь, какой сильный голос. Наконец, в версиях после 16.3.0 React официально предоставляет стабильный контекстный API.Примеры в этой статье основаны на контекстном API после версии 16.3.0.

концепция

Сначала поймите роль контекста и то, что такое провайдеры и потребители, и подумайте, какую проблему решает этот шаблон (межуровневое взаимодействие компонентов).

Контекст создает объект контекста и предоставляет его внешнему миру.提供者(通常在组件树中上层的位置)и消费者, все дочерние компоненты в контексте, Все могут получить доступ к данным в этом контексте, минуя реквизиты. Можно понять, что есть объект, который централизованно управляет состоянием, и ограничивает область доступа к этому объекту, Подкомпоненты внутри области могут получить свое внутреннее значение.

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

использование

Вот небольшой пример, функция заключается в переключении цвета темы. Эффект показан на рисунке:

В соответствии с вышеуказанными концепциями и функциями разбейте шаги, которые необходимо реализовать:

  • Создать контекст для предоставления нашим поставщикам и потребителям
  • Провайдер предоставляет данные
  • Потребители получают данные

Организация файлов здесь следующая:

├─context.js    // 存放context的文件
│─index.js      // 根组件,Provider所在的层级
│─Page.js       // 为了体现跨层级通信的添加的一个中间层级组件,子组件为Title和Paragraph
│─Title.js      // 消费者所在的层级
│─Paragraph.js  // 消费者所在的层级

создать контекст

import React from 'react'

const ThemeContext = React.createContext()

export const ThemeProvider = ThemeContext.Provider
export const ThemeConsumer = ThemeContext.Consumer

здесь,ThemeContextЭто созданный контекст, который содержит два свойства, как видно из названия, одно — поставщик, а другое — потребитель. Провайдер и Потребитель появляются парами, и каждый Провайдер соответствует Потребителю. И каждая пара создается React.createContext().

компонент страницы

Нечего сказать, это просто компонент контейнера

const Page = () => <>
  <Title/>
  <Paragraph/>
</>

Провайдер предоставляет данные

Провайдеры обычно располагаются на верхнем уровне, и значением, принимаемым ThemeProvider, является объект контекста, который он предоставляет.

// index.js
import { ThemeProvider } from './context'

render() {
  const { theme } = this.state
  return <ThemeProvider value={{ themeColor: theme }}>
    <Page/>
  </ThemeProvider>
}

Потребители получают данные

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

// Title.js 和 Paragraph的功能是一样的,代码也差不多,所以单放了Title.js
import React from 'react'
import { ThemeConsumer } from './context'
class Title extends React.Component {
  render() {
    return <ThemeConsumer>
      {
        theme => <h1 style={{ color: theme.themeColor }}>
          title
        </h1>
      }
    </ThemeConsumer>
  }
}

О вложенных контекстах

В этот момент у вас могут возникнуть сомнения, что в приложении невозможно иметь только один контекст. Что делать, если несколько контекстов вложены друг в друга?

Версии до v16.3.0

На самом деле контекст React в версиях до 16.3.0 был разработан с учетом этого сценария. Просто реализовать его немного сложнее. Давайте посмотрим на конкретное использование: В отличие от текущего использования, Provider и Consumer не создаются парами.

Provider — это обычный компонент, разумеется, он должен располагаться в верхнем слое компонента Consumer. Для его создания нам нужно использовать два метода:

  • getChildContext: предоставляет自身范围контекстные данные
  • childContextTypes: Объявление自身范围структура контекста
class ThemeProvider extends React.Component {
  getChildContext() {
    return {
      theme: this.props.value
    };
  }
  render() {
    return (
      <React.Fragment>
        {this.props.children}
      </React.Fragment>
    );
  }
}
ThemeProvider.childContextTypes = {
  theme: PropTypes.object
};

Посмотрите на потребителей, нужно использоватьcontextTypes, чтобы объявить структуру полученного контекста.

const Title = (props, context) => {
  const {textColor} = context.theme;
  return (
    <p style={{color: color}}>
      我是标题
    </p>
  );
};

Title.contextTypes = {
  theme: PropTypes.object
};

Окончательное использование:

<ThemeProvider value={{color: 'green' }} >
   <Title />
</ThemeProvider>

Вернемся к проблеме вложенности, вы видите, как ее решить?

Провайдер делает две вещи: предоставляет контекстные данные, а затем. Структура данных этой области контекста объявляется снова. Что касается Consumer, полученная структура данных контекста определяется contextTypes. Это эквивалентно тому, что Потребитель указывает, какую структуру данных получать, а данные этой структуры заранее определяются Провайдером. Таким образом, никакая вложенность не страшна, потребителю нужно только определить Хорошо получить заявленную кем структуру контекста. Если он не определен, данные контекста не могут быть получены.

Версии после v16.3.0

Версии после v16.3.0 намного проще в использовании, чем раньше. Способ решения проблемы вложенности также более элегантен. Поскольку Provider и Consumer создаются парами. Даже если поставщик одной пары является поставщиком другой пары Структура данных Потребителя такая же, как и тип значения, а Потребитель также разрешает доступ к контексту Провайдера. Это решение.

Суммировать

Для данного контекста это вещь. Я не думаю, что это много полезного в приложениях. Как и в случае с Provider в React-Redux или LocalProvider в antd, достаточно использовать его почти один раз, потому что слишком частое использование сделает приложение очень запутанным. Зависимости между компонентами становятся сложными. Но API, который нам предоставляет React, все еще может видеть, что он все еще хочет компенсировать недостатки своего управления состоянием, а useReducer в Hooks показывает это еще больше.