React без ES6

Обычно компонент React определяется как простой JavaScript-класс:

class Greeting extends React.Component {
  render() {
    return <h1>Привет, {this.props.name}</h1>;
  }
}

Если вы ещё не работаете с ES6, то можете использовать модуль create-react-class:

var createReactClass = require('create-react-class');
var Greeting = createReactClass({
  render: function() {
    return <h1>Привет, {this.props.name}</h1>;
  }
});

API ES6-классов похож на createReactClass() за некоторыми исключениями.

Объявление пропсов по умолчанию

С помощью функций и классов ES6 defaultProps определяется как свойство самого компонента:

class Greeting extends React.Component {
  // ...
}

Greeting.defaultProps = {
  name: 'Василиса'
};

При использовании createReactClass() вам нужно определить метод getDefaultProps() в переданном объекте:

var Greeting = createReactClass({
  getDefaultProps: function() {
    return {
      name: 'Василиса'
    };
  },

  // ...

});

Установка начального состояния

В ES6-классах вы можете определять начальное состояние через this.state в конструкторе:

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: props.initialCount};
  }
  // ...
}

При использовании createReactClass() вам придётся отдельно реализовать метод getInitialState, который возвращает начальное состояние:

var Counter = createReactClass({
  getInitialState: function() {
    return {count: this.props.initialCount};
  },
  // ...
});

Автоматическая привязка

В компонентах React, объявленных как классы ES6, методы следуют той же семантике, что и обычные классы ES6. Это означает, что они сами по себе не связывают this с экземпляром. Вам придётся явно использовать .bind(this) в конструкторе:

class SayHello extends React.Component {
  constructor(props) {
    super(props);
    this.state = {message: 'Привет!'};
    // Эта строка важна!
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    alert(this.state.message);
  }

  render() {
    // Мы можем использовать `this.handleClick` как обработчик событий, потому что он привязан
    return (
      <button onClick={this.handleClick}>
        Поздороваться
      </button>
    );
  }
}

Если вы используете createReactClass(), то это необязательно, так как все методы будут связаны:

var SayHello = createReactClass({
  getInitialState: function() {
    return {message: 'Привет!'};
  },

  handleClick: function() {
    alert(this.state.message);
  },

  render: function() {
    return (
      <button onClick={this.handleClick}>
        Поздороваться
      </button>
    );
  }
});

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

Если универсальный код для вас слишком неприглядный, вы можете включить экспериментальный синтаксис свойств класса с помощью Babel:

class SayHello extends React.Component {
  constructor(props) {
    super(props);
    this.state = {message: 'Привет!'};
  }
  // ВНИМАНИЕ! Этот синтаксис экспериментальный!
  // Здесь стрелочная функция выполняет привязку:
  handleClick = () => {
    alert(this.state.message);
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        Поздороваться
      </button>
    );
  }
}

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

Существует несколько безопасных вариантов:

  • привязывайте методы в конструкторе.
  • используйте стрелочные функции, например, onClick={(e) => this.handleClick(e)}.
  • продолжайте использовать createReactClass;

Примеси

Примечание:

ES6 запущен без поддержки примесей. Поэтому React не поддерживает примеси с классами ES6.

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

Этот раздел существует только для справки.

Иногда очень разные компоненты могут иметь общую функциональность. Иногда это называют сквозной функциональностью. createReactClass позволяет использовать для этого устаревшую систему mixins.

Одним из распространённых вариантов использования — когда вы собираетесь обновлять компонент через какой-то промежуток времени. Можно просто использовать setInterval(), но важно отменить процесс, когда он больше не нужен, чтобы сэкономить память. React предоставляет методы жизненного цикла, которые позволяют узнать, когда компонент будет создан или уничтожен. Давайте применим эти методы и создадим небольшую примесь, которая предоставляет функцию setInterval() и автоматически очищает мусор, когда компонент уничтожается.

var SetIntervalMixin = {
  componentWillMount: function() {
    this.intervals = [];
  },
  setInterval: function() {
    this.intervals.push(setInterval.apply(null, arguments));
  },
  componentWillUnmount: function() {
    this.intervals.forEach(clearInterval);
  }
};

var createReactClass = require('create-react-class');

var TickTock = createReactClass({
  mixins: [SetIntervalMixin], // Использовать примесь
  getInitialState: function() {
    return {seconds: 0};
  },
  componentDidMount: function() {
    this.setInterval(this.tick, 1000); // Вызвать метод на примеси
  },
  tick: function() {
    this.setState({seconds: this.state.seconds + 1});
  },
  render: function() {
    return (
      <p>
        React был выполнен за {this.state.seconds} секунд.
      </p>
    );
  }
});

ReactDOM.render(
  <TickTock />,
  document.getElementById('example')
);

Если компонент использует несколько примесей и они определяют один и тот же метод жизненного цикла (т.е. хотят выполнить некоторую очистку при уничтожении компонента), все методы жизненного цикла гарантированно будут вызваны. Методы, определённые на примесях, запускаются в том порядке, в котором они перечислены, а затем вызывается метод самого компонента.