React и ES6 - Часть 3, Биндинг методов в классах React (включая ES7)
React

React и ES6 - Часть 3, Биндинг методов в классах React (включая ES7)

Оригинал: React and ES6 - Part 3, Binding to methods of React class (ES7 included), Egor Smirnov

Это третья статья серии, в которой мы продолжаем рассматривать использование React c ECMAScript 6 и ECMAScript 7.

Если вы читали раздел «Метод render компонента CartItem» в предыдущей статье, вас возможно удивило использование {this.increaseQty.bind(this)}.

Если мы попытаемся заменить эту конструкцию на {this.increaseQty}, мы увидим ошибку «Uncaught TypeError: Cannot read property ’setState’ of undefined» в консоли:

Дело в том, что, когда мы вызываем функцию таким способом, она вызывается не в контексте класса, а значением this является undefined. Это стандартное поведение JavaScript и вполне ожидаемо. В случае использования React.createClass() все методы автоматически связываются с созданным объектом. Это может показаться нелогичным для некоторых разработчиков.

Когда команда React реализовывала поддержку ES6, они решили отказаться от автоматического связывания. Причины этого вы можете узнать в статье.

Давайте рассмотрим различные способы вызова метода в JSX в случае использования ES6 классов. Все эти способы доступны в репозитории на GitHub.

Способ 1. Использование Function.prototype.bind().

Мы с ним уже встречались:


export default class CartItem extends React.Component {
    render() {
        
    }
}

Поскольку любой метод ES6 является обычной функцией JavaScript, он наследует метод bind() прототипа Function. Поэтому теперь, когда мы вызовем increaseQty() в JSX, this будет ссылаться на экземпляр класса. Подробнее в Function.prototype.bind() вы можете прочитать в статье на MDN.

Способ 2. Использование функции, определенной в конструкторе.

Этот способ является комбинацией предыдущего способа с использованием конструктора класса:


export default class CartItem extends React.Component {
    
    constructor(props) {
        super(props);
        this.increaseQty = this.increaseQty.bind(this);
    }

    render() {
        <button onClick={this.increaseQty} className="button success">+</button>
    }
}

Вам больше не нужно использовать bind() в JSX, но это немного увеличивает код конструктора.

Способ 3. Использование стрелочной функции и конструктора.

Стрелочные функции ES6 сохраняют контекст выполнения при вызове. Вы можете использовать эту особенность и определить increaseQty() в конструкторе следующим образом:


export default class CartItem extends React.Component {
    
    constructor(props) {
        super(props);
        this.increaseQty = () => this.increaseQty();
    }

    render() {
        
    }
}

Способ 4. Использование стрелочных функций и свойств класса ES7.

Дополнительно, вы можете использовать стрелочные функции в комбинации с свойствами класса ES7:


export default class CartItem extends React.Component {
      
    increaseQty = () => this.increaseQty();

    render() {
        <button onClick={this.increaseQty} className="button success">+</button>
    }
}

Таким образом, вместо определения метода класса в конструкторе, как в способе 3, мы использовали инициализатор свойства.

Обратите внимание: свойства класса еще не являются частью стандарта JavaScript. Но вы можете пользоваться ими, подключив пресет Babel stage-0. Подробнее об этом можно прочитать в документации Babel.

Мы уже переключались в режим stage 0 во второй части серии, поэтому в данном случае я не буду это объяснять.

Способ 5. Использование синтаксиса биндинга ES7.

Совсем недавно в Babel реализовали поддержку синтаксического сахара для Function.prototype.bind() с использованием ::. Я не буду вдаваться здесь в подробности о том, как он работает. Уже есть довольно хорошее объяснение. Дополнительную информацию вы можете получить из статьи в официальном блоге Babel.

Ниже показан код с использованием синтаксиса биндинга ES7:


export default class CartItem extends React.Component {
    
    constructor(props) {
        super(props);
        this.increaseQty = ::this.increaseQty;
        // line above is an equivalent to this.increaseQty = this.increaseQty.bind(this);
    }

    render() {
        <button onClick={this.increaseQty} className="button success">+</button>
    }
}

Эта особенность является экспериментальной.

Способ 6. Использование ES7 биндинга в JSX.

Вы также можете использовать возможности ES7 биндинга непосредственно в JSX файле. Например:


export default class CartItem extends React.Component {
    render() {
        <button onClick={::this.increaseQty} className="button success">+</button>
    }
}

Если кратко, единственным недостатком этого способа является то, что функция пересоздается каждый раз при перерисовке компонента. Это не оптимально. И что более важно, это вызовет проблемы, если вы используете что-то вроде PureRenderMixin (или его эквивалент для классов ES6).

Заключение

В той статье мы рассмотрели различные способы биндинга методов класса в компонентах React. Я подготовил тестовый проект, основанный на части 2 серии. Он доступен здесь.

В следующий раз мы рассмотрим, как обстоят дела с миксинами React в ES6 классах.

Для дополнительного чтения

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