React - это библиотека для создания интерфейсных представлений. Он имеет большую экосистему библиотек, которые с ним работают. Кроме того, мы можем использовать его для улучшения существующих приложений.

Компоненты React могут передавать данные от родителя к потомку только через props. Контекстный API дополняет это, позволяя компонентам с другими отношениями обмениваться данными.

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

Когда использовать контекст

Мы должны использовать Context для обмена данными между компонентами React. Однако его следует использовать с осторожностью, поскольку он создает тесную связь между компонентами.

Чтобы использовать его в простом приложении, мы можем написать следующее:

const ColorContext = React.createContext("green");
class Button extends React.Component {
  render() {
    return (
      <div>
        <ColorContext.Consumer>
          {value => (
            <button style={{ color: value }}>{this.props.children}</button>
          )}
        </ColorContext.Consumer>
      </div>
    );
  }
}
class App extends React.Component {
  render() {
    return (
      <div>
        <ColorContext.Provider value="blue">
          <Button>Click Me</Button>
        </ColorContext.Provider>
      </div>
    );
  }
}

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

const ColorContext = React.createContext("green");

createContext принимает значение по умолчанию в качестве аргумента, где мы передали 'green'.

Затем в компоненте App у нас есть компонент ColorContext.Provider с параметром value, установленным на значение, которым мы хотим поделиться.

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

В этом случае мы создали новый компонент Button, который имеет компонент ColorContext.Consumer. Внутри него мы можем получить значение, предоставленное поставщиком контекста, из параметра value в функции, которую мы вставили в компонент ColorContext.Consumer.

value должен быть установлен в 'blue', поскольку это то, что мы установили в качестве значения value prop.

Внутри функции, которую мы передали потребителю, мы вернули элемент buttom с опорой стиля и установили стиль color на value, то есть 'blue'.

Альтернативы контексту

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

Например, если мы хотим передать свойство цвета Button компонентам, которое содержится в ButtonBar. Мы можем сделать это следующим образом:

class Button extends React.Component {
  render() {
    return (
      <button style={{ color: this.props.color }}>{this.props.children}</button>
    );
  }
}
class ButtonBar extends React.Component {
  render() {
    return this.props.buttons;
  }
}
class App extends React.Component {
  render() {
    const buttons = [
      <Button color="blue">Click Me</Button>,
      <Button color="green">Click Me 2</Button>
    ];
    return <ButtonBar buttons={buttons} />;
  }
}

В компоненте App у нас есть компоненты Button в массиве buttons. Затем мы передали весь массив прямо в компонент ButtonBar.

Затем ButtonBar просто возвращает то, что мы передали, то есть this.props.buttons.

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

Обновление контекста из вложенного компонента

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

Например, мы можем написать следующее:

const colorObj = {
  color: "green",
  toggleColor: () => {}
};
const ColorContext = React.createContext(colorObj);
class Button extends React.Component {
  render() {
    return (
      <div>
        <ColorContext.Consumer>
          {({ color, toggleColor }) => (
            <button onClick={toggleColor} style={{ color }}>
              {this.props.children}
            </button>
          )}
        </ColorContext.Consumer>
      </div>
    );
  }
}
class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      color: "blue",
      toggleColor: () => {
        this.setState(state => ({
          color: state.color === "green" ? "blue" : "green"
        }));
      }
    };
  }
  render() {
    return (
      <div>
        <ColorContext.Provider value={this.state}>
          <Button>Click Me</Button>
        </ColorContext.Provider>
      </div>
    );
  }
}

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

Затем в компоненте App мы инициализируем this.state, устанавливая его для объекта с функцией toggleColor, а для свойства color установлено значение 'blue'.

Мы передаем this.state как значение свойства value для ColorContext.Provider.

Затем мы получаем доступ ко всему объекту внутри компонента ColorContext.Consumer в компоненте Button.

Внутри мы получаем свойства color и toggleColor из this.state, которые мы передали из ColorContext.Provider. Затем мы передаем toggleColor в свойство onClick и color в объект, который мы передали в свойство style.

Затем, когда мы нажимаем кнопку Click Me, цвет текста будет переключаться между синим и зеленым.

Использование нескольких контекстов

Мы можем использовать несколько контекстов, вложив их. Например, мы можем сделать это следующим образом:

const ColorContext = React.createContext("green");
const BorderContext = React.createContext("");
class Button extends React.Component {
  render() {
    return (
      <div>
        <ColorContext.Consumer>
          {color => (
            <BorderContext.Consumer>
              {border => (
                <button style={{ color, border }}>{this.props.children}</button>
              )}
            </BorderContext.Consumer>
          )}
        </ColorContext.Consumer>
      </div>
    );
  }
}
class App extends React.Component {
  render() {
    return (
      <div>
        <ColorContext.Provider value="blue">
          <BorderContext.Provider value="3px solid green">
            <Button>Click Me</Button>
          </BorderContext.Provider>
        </ColorContext.Provider>
      </div>
    );
  }
}

В приведенном выше коде мы создаем 2 контекста, ColorContext и BorderContext и передаем значения в опору value для обоих. Мы вложили поставщиков в компонент App, что означает, что оба контекста могут использоваться компонентом Button внутри.

Затем в компоненте Button у нас есть потребители для обоих контекстов, вложенных друг в друга. И тогда мы можем получить оба значения, которые были переданы от провайдеров.

Затем мы используем оба значения для установки стилей button.

В итоге мы получили кнопку с синим текстом и толстой зеленой рамкой.

Заключение

Мы можем использовать React Context API для обмена данными между компонентами.

Он работает путем создания объекта Context с React.createContext. Затем мы оборачиваем компонент поставщика контекста за пределы компонентов, из которых мы хотим использовать контекст.

Затем в компоненте, который мы помещаем в поставщик, у нас есть компонент-потребитель контекста, обернутый вне того, к чему мы хотим применить значение контекста.

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