React и ES6 - Часть 2, Классы React и инициализаторы свойств ES7
React

React и ES6 - Часть 2, Классы React и инициализаторы свойств ES7

Оригинал: React and ES6 - Part 2, React Classes and ES7 Property Initializers, Egor Smirnov

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

Другие части вы можете найти по ссылкам:

Код из этой статьи доступен на GitHub.

В первой статье мы начали с введения в ES6 и создания статичного компонента React, выводящего «Hello from ES6». Не очень интересно :)

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

Вот что мы увидим в результате:

Создание файла index.html

Начнем с создания простого HTML-файла:


<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>React and ES6 Part 2</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.2/css/foundation.min.css">
</head>
<body>
<div class="root"></div>
<script src="dist/bundle.js"></script>
</body>
</html>

Обратите внимание, что мы подключили фреймворк Foundation CSS с CDN. Это сделает наше приложение немного более симпотичным. Элемент div.root будет использоваться как контейнер для React приложения.

Gulpfile.js

Просто скопируйте содержимое gulpfile.js из предыдущей части. Никаких изменений на данном этапе не понадобиться.

Также скопируйте package.json.

Далее, запустите команду npm install (чтобы установить зависимости) и затем команду gulp. Gulp будет непрерывно следить за изменениями в файлах.

Основной компонент приложения

Создайте app.jsx:


import React from 'react';
import CartItem from './cartItem';

const order = {
    title: 'Fresh fruits package',
    image: 'http://images.all-free-download.com/images/graphiclarge/citrus_fruit_184416.jpg',
    initialQty: 3,
    price: 8
};

React.render(
    <CartItem title={order.title} 
        image={order.image} 
        initialQty={order.initialQty} 
        price={order.price}/>,
    document.querySelector('.root')
);

Вот что здесь происходит:

Строка 1. Мы импортируем библиотеку React и инициализируем переменную React.

Строка 2. Импортируем компонент CartItem, мы создадим его позже.

Строки 4-9. Передаем свойства в компонент CartItem (включая заголовок товара, изображение, начальную стоимость и цену).

Строки 11-17. Монтируем компонент CartItem в элемент DOM по классу .root.

Структура компонента CartItem

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

Добавьте следующий код в файл cartItem.jsx:


import React from 'react';

export default class CartItem extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            qty: props.initialQty,
            total: 0
        };
    }
    componentWillMount() {
        this.recalculateTotal();
    }
    increaseQty() {
        this.setState({qty: this.state.qty + 1}, this.recalculateTotal);
    }
    decreaseQty() {
        let newQty = this.state.qty > 0 ? this.state.qty - 1 : 0;
        this.setState({qty: newQty}, this.recalculateTotal);
    }
    recalculateTotal() {
        this.setState({total: this.state.qty * this.props.price});
    }
}

Объяснение:

Строки 4-10. Это конструктор, появившийся в классах React. Первое, на что следует обратить внимание — это вызов функции super(props), он является обязательным. Следующий момент — вместо метода getInitialState(), разработчики React рекомендуют использовать своство this.state. Мы инициализировали состояние свойствами, переданными из app.jsx. Лично мне нравится это улучшение.

Строки 11-13. Объявление метода componentWillMount() (который в конечном счете является методом класса CartItem). В этом методе мы длаем пересчет общей стоимости. Чтобы рассчитать общую стоимость, мы используем метод recalculatePrice(), который умножает количество (хранится в состоянии компонента) на цену (является свойством).

Строки 14-20. Методы для увеличения или уменьшения количества элементов. Эти методы будут вызываться по нажатию на кнопки (см. скриншот приложения в начале статьи). Также обратите внимание, что мы используем в качестве функции обратного вызова метода this.setState() метод пересчета общей стоимости.

Метод render компонента CartItem

Добавьте следующий метод в класс CartItem:


export default class CartItem extends React.Component {

    // previous code we wrote here
    
    render() {
        return <article className="row large-4">
            <figure className="text-center">
                <p>
                    <img src={this.state.image}/>
                </p>
                <figcaption>
                    <h2>{this.state.title}</h2>
                </figcaption>
            </figure>
            <p className="large-4 column"><strong>Quantity: {this.state.qty}</strong></p>

            <p className="large-4 column">
                <button onClick={this.increaseQty.bind(this)} className="button success">+</button>
                <button onClick={this.decreaseQty.bind(this)} className="button alert">-</button>
            </p>

            <p className="large-4 column"><strong>Price per item:</strong> ${this.state.price}</p>

            <h3 className="large-12 column text-center">
                Total: ${this.state.total}
            </h3>

        </article>;
    }
}

Здесь мы используем только теги в формате JSX + состояние компонента и применяем Foundation CSS для декорирования.

Не беспокойтесь по поводу {this.increaseQty.bind(this)} — я объясню это в следующей статье серии. А пока просто поверьте мне, что эта строка будет вызывать метод increaseQty() класса CartItem.

Итак, теперь у нас симпотичное приложение, взаимодействующее с пользователем. Не смотря на то, что это простой пример, он показывает, как написать компонент React, используя ECMAScript 6. Лично мне нравится новый синтаксис ES6.

Мы еще не закончили. Перед тем, как закончить статью, я хотел бы взглянуть на пару дополнительных функций ES6.

Default Props и Prop Types в ES6 классах React

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

К счастью, это поддерживается React и называется Default Props и Prop Types. Вы можете познакомиться с ними поближе здесь.

После класса CartItem добавьте следующие строки:


CartItem.propTypes = {
    title: React.PropTypes.string.isRequired,
    price: React.PropTypes.number.isRequired,
    initialQty: React.PropTypes.number
};
CartItem.defaultProps = {
    title: 'Undefined Product',
    price: 100,
    initialQty: 0
};

В результате, если вы попытаетесь передать число в качестве свойства title, вы получите предупреждение в консоли. В этом сила валидации свойств React.

Добавление ES7 в проект

Вы можете задать вполне резонный вопрос — зачем я буду думать о ES7, когда ES6 только стандартизирован?

Я отвечу, что это взгляд в будущее. И начало использования таких особенностей, как инициализаторы свойств и декораторы.

Несмотря на то, что ES7 пока находится на совсем ранней стадии, есть ряд особенностей, которые уже реализованы в Babel. Эти экспериментальные особенности транслируются с ES7 в валидный ES5. Круто! Вы можете прочитать подробнее об этих экспериментальных особенностях на странице.

Чтобы добавить синтаксический сахар, нам необходимо внести небольшое изменение в строку 8 в gulpfile.js. Замените эту строку на код ниже:


.transform(babelify.configure({stage: 0}))

Полный gulpfile.js вы можете взять из репозитория на GitHub.

Инициализаторы свойств ES7 для Default Props и Prop Types компонента React

В класс CartItem добавьте следующий код перед конструктором:


export default class CartItem extends React.Component {
    static propTypes = {
        title: React.PropTypes.string.isRequired,
        price: React.PropTypes.number.isRequired,
        initialQty: React.PropTypes.number
    };    
    static defaultProps = {
        title: 'Undefined Product',
        price: 100,
        initialQty: 0
    };
    
    constructor() {
        ...
    }
    
    // .. all other code
}

Этот код делает то же самое, что и код после определения класса, только в немного более аккуратным способом (на мой взгляд). Вы можете удалить код, относящийся к propTypes / defaultProps, который идет после определения класса CartItem, поскольку он больше не нужен.

Инициализаторы свойств ES7 для начального состояния компонента

Финальным шагом будет перенос начального состояния компонента из конструктора в инициализатор свойства.

Добавьте следующий код перед конструктором класса CartItem:


export default class CartItem extends React.Component {
    // .. some code here
    state = {
        qty: this.props.initialQty,
        total: 0
    };
    // .. constructor starts here

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

Готово. Вы можете взять финальный вариант cartItem.jsx из этой части серии на GitHub.

Заключение

В этой статье мы познакомились с реализацией компонентов React на ES6, а также кратко рассмотрели инициализаторы свойств ES7.

Следите за серией ES6 + React!

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

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