Рассылка
Подпишитесь на рассылку и получайте дайджест новостей и статей.
Никакого спама!
Подписаться

Справочник ReactJS

Контекст

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

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

Обратите внимание:

Контекст — это дополнительная экспериментальная особенность. API может измениться в будущих версиях.

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

Если вы все же используете контекст — делайте это по минимуму.

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

Передача данных по дереву

Предположим, у нас есть следующая структура:


var Button = React.createClass({
  render: function() {
    return (
      <button style={{background: this.props.color}}>
        {this.props.children}
      </button>
    );
  }
});

var Message = React.createClass({
  render: function() {
    return (
      <div>
        {this.props.text} <Button color={this.props.color}>Delete</Button>
      </div>
    );
  }
});

var MessageList = React.createClass({
  render: function() {
    var color = "purple";
    var children = this.props.messages.map(function(message) {
      return <Message text={message.text} color={color} />;
    });
    return <div>{children}</div>;
  }
});

В этом примере мы вручную передаем свойство color для стилизации компонента Button. Темизация — это хороший пример ситуации, когда модет понадобиться, чтобы все поддерево имело доступ к некоей информации (в данном случае к цвету). Используя контекст, мы можем сделать это автоматически:


var Button = React.createClass({
  contextTypes: {
    color: React.PropTypes.string
  },
  render: function() {
    return (
      <button style={{background: this.context.color}}>
        {this.props.children}
      </button>
    );
  }
});

var Message = React.createClass({
  render: function() {
    return (
      <div>
        {this.props.text} <Button>Delete</Button>
      </div>
    );
  }
});

var MessageList = React.createClass({
  childContextTypes: {
    color: React.PropTypes.string
  },
  getChildContext: function() {
    return {color: "purple"};
  },
  render: function() {
    var children = this.props.messages.map(function(message) {
      return <Message text={message.text} />;
    });
    return <div>{children}</div>;
  }
});

Добавление childContextTypes и getChildContext в компонент MessageList (поставщик контекста) автоматически передает данные вниз и компонент в поддереве (в данном случае, компонент Button) может их получить с помощью contextTypes.

Если contextTypes не определен, this.context будет содержать пустой объект.

Связывание родитель-ребенок

Контекст также позволяет реализовывать API по типу такого:


<Menu>
  <MenuItem>aubergine</MenuItem>
  <MenuItem>butternut squash</MenuItem>
  <MenuItem>clementine</MenuItem>
</Menu>

Используя контекст, каждый MenuItem может обмениваться данными с компонентом, содержащий меню.

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


<Menu items={['aubergine', 'butternut squash', 'clementine']} />

Напомним, что бы можете передавать целын компоненты в качестве ствойства.

Обращение к контексту в методах жизненного цикла

Если в компоненте определен contextTypes, следующие методы жизненного цикла получают дополнительный параметр — объект контекста:


void componentWillReceiveProps(
  object nextProps, object nextContext
)

boolean shouldComponentUpdate(
  object nextProps, object nextState, object nextContext
)

void componentWillUpdate(
  object nextProps, object nextState, object nextContext
)

void componentDidUpdate(
  object prevProps, object prevState, object prevContext
)

Обращение к контексту в stateless компонентах

В stateless компонентах также можно получить доступ к контексту, если contextTypes определен как свойство функции. Следующий код показывает, как можно реализовать компонент Button в виде stateless компонента.


function Button(props, context) {
  return (
    <button style={{background: context.color}}>
      {props.children}
    </button>
  );
}
Button.contextTypes = {color: React.PropTypes.string};

Обновление контекста

Функция getChildContext будет вызываться при изменении свойства или состояния. Для того, чтобы обновить данные в контексте, необходимо локально обновить состояние с помощью this.setState. Это создаст новый контекст и изменения будут получены детьми.


var MediaQuery = React.createClass({
  getInitialState: function(){
    return {type:'desktop'};
  },
  childContextTypes: {
    type: React.PropTypes.string
  },
  getChildContext: function() {
    return {type: this.state.type};
  },
  componentDidMount: function(){
    var checkMediaQuery = function(){
      var type = window.matchMedia("(min-width: 1025px)").matches ? 'desktop' : 'mobile';
      if (type !== this.state.type){
        this.setState({type:type});
      }
    };

    window.addEventListener('resize', checkMediaQuery);
    checkMediaQuery();
  },
  render: function(){
    return this.props.children;
  }
});

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

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

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

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

Известные ограничения

Если компонент, предоставляющий контекст, возвращает false в shouldComponentUpdate, то потомки не получат новое значение контекста. См. #2517 для большей информации.