React — это крутой фреймворк, созданный группой крутых программистов в Facebook. Он реализует виртуальный DOM, используя подход, подобный DOM, чтобы мгновенно добавлять нужные вам компоненты и мгновенно удалять ненужные. React играет роль V в структуре MVC, но если объединить его с Flux, вы получите крутой фреймворк, который легко синхронизирует M и V. Подробнее о Flux позже~

Компоненты

В React вы можете создать компонент со специальной функциональностью, которую не найдёте в HTML-элементах, даже если искать с фонарём, например выпадающую навигацию в этом руководстве. У каждого компонента есть своя территория (область видимости), поэтому, определив компонент один раз, мы можем повторно использовать его снова и снова, не взаимодействуя с другими компонентами!

У каждого компонента есть метод render, который эффективно возвращает HTML для вывода в браузер. Мы также можем вызывать другие React-компоненты, а это значит, что стоит только войти в React — и это глубоко, как море... ой, нет, «нет ничего невозможного, есть только невообразимое».

JSX

Если вы внимательно присмотритесь к React, то можете заметить нечто под названием JSX. JSX позволяет нам писать HTML в JavaScript, вместо того чтобы использовать HTML для размещения JavaScript. Это помогает нам быстро разрабатывать, потому что не нужно беспокоиться о строках, переносах строк и тому подобном. Вы можете запускать JSX в браузере, но это не рекомендуется, потому что это замедлит вашу страницу. Gulp и Grunt предоставляют интерпретатор JSX для ваших задач предварительной обработки, поэтому, если вы хотите использовать JSX, я рекомендую включить эту функцию.

Использование JSX

Как упоминалось ранее, в JSX у каждого компонента есть метод render, который создаёт «ViewModel» — перед возвратом HTML компоненту вы можете поместить информацию из этой модели представления в представление, то есть ваш HTML будет динамически изменяться на основе этой модели (например, динамический список).

Когда вы закончите, вы можете вернуть всё, что хотите отрисовать. Использовать JSX для этого очень просто.

var ExampleComponent = React.createClass({
    render: function () {
        return (
            <div className="navigation">
                Hello World!
            </div>
            );
    }
});

Если вы запустите этот код в браузере, вы получите только синтаксическую ошибку, потому что в JavaScript символы < и > должны быть заключены в кавычки. Когда вы запускаете интерпретатор JSX для этого кода, он преобразуется в это.

var ExampleComponent = React.createClass({
    render: function () {
        return (
            React.createElement('div', {className: 'navigation'}, 'Hello World!')
            );
    }
});

Вы можете нажать здесь, чтобы протестировать пример.— Я использую браузерный интерпретатор JSX (не рекомендуется, но для JSFiddle).

Это сработало! JSX интерпретирует то, что вы сгенерировали с помощью React.createElementвсеDOM-узлы, создавая типы узлов, параметры и содержимое. Можно обойтись без JSX, но это означает, что вам придётся вручную написать все DOM-узлы через React.createElement. Бесчисленные примеры говорят мне использовать JSX.

Вы, должно быть, задаётесь вопросом, почему я использую className вместо class в DOM. Это потому, что class — зарезервированное слово в JavaScript. Когда JSXинтерпретируетваш код, он превращает все атрибуты узла в объект, но вы не можете обращаться с объектом как с атрибутом!

Использование переменных в props

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

var ExampleComponent = React.createClass({
render: function () {
    var navigationClass = 'navigation';
    return (
        <div className={ navigationClass }>
            Hello World!
        </div>
        );
}
});

Вы можете увидеть эту возможность здесь.

Первоначальный рендеринг

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

var ExampleComponent = React.createClass({
render: function () {
    return (
        <div className="navigation">
            Hello World!
        </div>
        );
}
});
React.render(<ExampleContent />, document.body);

Он отрендерит компонент в узле body — просто, правда! С этого момента вы можете вызывать другие компоненты как обычно или, если хотите, использовать функцию render много раз, если не хотите использовать React для рендеринга всей страницы, но всё ещё хотите использовать несколько компонентов.

Основы компонента

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

Когда вы передаёте свойства компоненту, они называются props. Не ограничивайтесь HTML-атрибутами; вы можете передать что угодно и получить к этому доступ внутри компонента через this.props. Это позволяет нам повторно использовать один и тот же компонент, но передавать другой набор props, например «конфигурацию» компонента.

Пропсы

В нашем предыдущем примере «Hello World!» у HTML-узла был атрибут className. Внутри компонента мы можем получить доступ к этому значению с помощью this.props.classname, но, как упоминалось ранее, вы можете передать что угодно. Для нашего выпадающего меню нам нужно настроить навигацию как объект, который компонент будет использовать в качестве конфигурации для рендеринга. Давайте начнём—

var navigationConfig = [];

var Navigation = React.createClass({
    render: function () {
        return (
            <div className="navigation">

            </div>
            );
    }
});

React.render(<Navigation config={ navigationConfig } />, document.body);

Если мы сейчас обратимся к this.props.config, мы получим пустой массив (значение navigationConfig). Прежде чем перейти к фактическому коду навигации, давайте объясним state.

State

Как обсуждалось ранее, у каждого компонента есть собственный «state». При использовании state вы сначала определяете начальное состояние, и только потом можете использовать this.setState для его обновления. Всякий раз, когда состояние обновляется, компонент снова вызывает функцию render, используя новые значения, чтобы заменить или изменить то, что было отрендерено ранее. В этом суть виртуального DOM — алгоритм сравнения выполняется внутри React, поэтому нам не нужно полагаться на обновления DOM (потому что DOM медленный). React вычисляет различия и создаёт набор инструкций (например, добавляет класс «active» к «navigation__link» или удаляет узел), а затем выполняет их в DOM.

Для навигации мы храним состояние открытости выпадающего меню в state. Для этого добавьте функцию getInitialState в объект конфигурации класса и верните объект с нужным начальным состоянием.

var navigationConfig = [];

var Navigation = React.createClass({
    getInitialState: function () {
        return {
            openDropdown: -1
        };
    },
    render: function () {
        return (
            <div className="navigation">

            </div>
            );
    }
});

React.render(<Navigation config={ navigationConfig } />, document.body);

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

Мы можем получить доступ к состоянию через this.state, так что если мы посмотрим на this.state.openDropdown, то должны получить -1.

Жизненный цикл компонента

У каждого компонента есть свой «жизненный цикл» — ряд функций, которые вы можете определить в конфигурации компонента и которые будут вызываться на протяжении жизни компонента. Мы уже видели getinitialstate — он вызывается только один раз, когда компонент монтируется.

componentWillMount

Эта функция вызывается, когда компонент вот-вот будет смонтирован. Это значит, что мы можем выполнить здесь код, необходимый для работы компонента. Поскольку render вызывается несколько раз в течение жизненного цикла компонента, мы обычно размещаем здесь код, который должен выполниться только один раз, например XHR-запросы.

var ExampleComponent = React.createClass({
    componentWillMount: function () {
        // xhr request here to get data
    },
    render: function () {
        // this gets called many times in a components life
        return (
            <div>
                Hello World!
            </div>
            );
    }
});

componentDidMount

После того как ваш компонент выполнит функцию render и действительно отрисуется в DOM, будет вызван componentDidMount. Здесь мы можем выполнить любые необходимые операции с DOM, а также всё, что зависит от того, что компонент уже существует в DOM, например отрисовку диаграммы. Вы можете получить доступ к DOM-узлу изнутри, вызвав this.getDOMNode.

var ExampleComponent = React.createClass({
    componentDidMount: function () {
        var node = this.getDOMNode();
        // render a chart on the DOM node
    },
    render: function () {
        return (
            <div>
                Hello World!
            </div>
            );
    }
});

componentWillUnmount

Эта функция будет вызвана, когда вы собираетесь удалить компонент из DOM. Это позволяет нам прибрать за компонентом, например удалить все прослушиватели событий, которые мы привязали. Если мынеприберём за собой, то когда одно из этих событий сработает, оно попытается выполнить вычисления на компоненте, который не был смонтирован, и React выдаст ошибку.

var ExampleComponent = React.createClass({
    componentWillUnmount: function () {
        // unbind any event listeners specific to this component
    },
    render: function () {
        return (
            <div>
                Hello World!
            </div>
            );
    }
});

Методы компонента

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

getDefaultProps

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

Когда вы определяете компонент, эти props по умолчанию кэшируются, поэтому они одинаковы для каждого экземпляра компонента и не могут быть изменены. Для компонента навигации мы задали конфигурацию как пустой массив, поэтому даже если конфигурация не передана, в методе render не возникнет ошибка.

var Navigation = React.createClass({
    getInitialState: function () {
        return {
            openDropdown: -1
        };
    },
    getDefaultProps: function () {
        return {
            config: []
        }
    },
    render: function () {
        return (
            <div className="navigation">

            </div>
            );
    }
});

React.render(<Navigation config={ navigationConfig } />, document.body);

propTypes

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

var Navigation = React.createClass({
    getInitialState: function () {
        return {
            openDropdown: -1
        };
    },
    getDefaultProps: function () {
        return {
            config: []
        }
    },
    propTypes: {
        config: React.PropTypes.array
    },
    render: function () {
        return (
            <div className="navigation">

            </div>
            );
    }
});

React.render(<Navigation config={ navigationConfig } />, document.body);

mixins

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

var ExampleMixin = {
    componentDidMount: function () {
        // bind some event listeners here
    },
    componentWillUnmount: function () {
        // unbind those events here!
    }
};

var ExampleComponent = React.createClass({
    mixins: [ExampleMixin],
    render: function () {
        return (
            <div>
                Hello World!
            </div>
            );
    }
});

var AnotherComponent = React.createClass({
    mixins: [ExampleMixin],
    render: function () {
        return (
            <div>
                Hello World!
            </div>
            );
    }
});

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

Циклы итерации

Когда у нас есть массив, содержащий объекты, как нам перебрать этот массив и отрендерить каждый объект в элементы списка? JSX позволяет использовать его в любом файле JavaScript. Вы можете применить map к массиву и вернуть JSX, а затем использовать React для его рендеринга.

var navigationConfig = [
    {
        href: 'http://ryanclark.me',
        text: 'My Website'
    }
];
var Navigation = React.createClass({
    getInitialState: function () {
        return {
            openDropdown: -1
        };
    },
    getDefaultProps: function () {
        return {
            config: []
        }
    },
    propTypes: {
        config: React.PropTypes.array
    },
    render: function () {
        var config = this.props.config;
        var items = config.map(function (item) {
            return (
                <li className="navigation__item">
                    <a className="navigation__link" href={ item.href }>
                        { item.text }
                    </a>
                </li>
                );
        });
        return (
            <div className="navigation">
                { items }
            </div>
            );
    }
});
React.render(<Navigation config={ navigationConfig } />, document.body);

Не стесняйтесь использовать navigationConfig в JSFiddle.

Конфигурация навигации состоит из массива и объектов, включая атрибут href, указывающий на гиперссылку, и атрибут text для отображения. Когда мы выполняем map, происходит перебор массива по одному элементу для получения объектов. Мы можем получить доступ к href и text и использовать их в HTML. При возврате списка все элементы списка в этом массиве будут заменены, поэтому, когда мы передаём его в React для обработки, он знает, как его отрендерить!

Миксины

Пока что мы добились раскрытия всех выпадающих списков. Нам нужно знать, какой элемент раскрывается. Мы будем использовать свойство .children для обхода нашего массива navigationConfig. Далее мы можем перебрать дочерние элементы выпадающего списка.

var navigationConfig = [
    {
        href: 'http://ryanclark.me',
        text: 'My Website',
        children: [
            {
                href: 'http://ryanclark.me/how-angularjs-implements-dirty-checking/',
                text: 'Angular Dirty Checking'
            },
            {
                href: 'http://ryanclark.me/getting-started-with-react/',
                text: 'React'
            }
        ]
    }
];
var Navigation = React.createClass({
    getInitialState: function () {
        return {
            openDropdown: -1
        };
    },
    getDefaultProps: function () {
        return {
            config: []
        }
    },
    propTypes: {
        config: React.PropTypes.array
    },
    render: function () {
        var config = this.props.config;
        var items = config.map(function (item) {
            var children, dropdown;
            if (item.children) {
                children = item.children.map(function (child) {
                    return (
                        <li className="navigation__dropdown__item">
                            <a className="navigation__dropdown__link" href={ child.href }>
                                { child.text }
                            </a>
                        </li>
                    );
                });
                dropdown = (
                    <ul className="navigation__dropdown">
                        { children }
                    </ul>
                );
            }
            return (
                <li className="navigation__item">
                    <a className="navigation__link" href={ item.href }>
                        { item.text }
                    </a>
                    { dropdown }
                </li>
                );
        });
        return (
            <div className="navigation">
                { items }
            </div>
            );
    }
});
React.render(<Navigation config={ navigationConfig } />, document.body);

Пример здесь- Но мы всё ещё видим элементы выпадающего списка, хотя мы установилиopenDropdown установили его в-1 。

Мы можем получить доступ в компонентеthis.state, чтобы определить, открыт ли выпадающий список, и мы можем добавить к нему новый класс CSS-стиля, чтобы показать эффект наведения.

var navigationConfig = [
    {
        href: 'http://ryanclark.me',
        text: 'My Website',
        children: [
            {
                href: 'http://ryanclark.me/how-angularjs-implements-dirty-checking/',
                text: 'Angular Dirty Checking'
            },
            {
                href: 'http://ryanclark.me/getting-started-with-react/',
                text: 'React'
            }
        ]
    }
];
var Navigation = React.createClass({
    getInitialState: function () {
        return {
            openDropdown: -1
        };
    },
    getDefaultProps: function () {
        return {
            config: []
        }
    },
    openDropdown: function (id) {
        this.setState({
            openDropdown: id
        });
    },
    closeDropdown: function () {
        this.setState({
            openDropdown: -1
        });
    },
    propTypes: {
        config: React.PropTypes.array
    },
    render: function () {
        var config = this.props.config;
        var items = config.map(function (item, index) {
            var children, dropdown;
            if (item.children) {
                children = item.children.map(function (child) {
                    return (
                        <li className="navigation__dropdown__item">
                            <a className="navigation__dropdown__link" href={ child.href }>
                                { child.text }
                            </a>
                        </li>
                    );
                });
                var dropdownClass = 'navigation__dropdown';
                if (this.state.openDropdown === index) {
                    dropdownClass += ' navigation__dropdown--open';
                }
                console.log(this.state.openDropdown, index);
                dropdown = (
                    <ul className={ dropdownClass }>
                        { children }
                    </ul>
                );
            }
            return (
                <li className="navigation__item" onMouseOut={ this.closeDropdown } onMouseOver={ this.openDropdown.bind(this, index) }>
                    <a className="navigation__link" href={ item.href }>
                        { item.text }
                    </a>
                    { dropdown }
                </li>
                );
        }, this);
        return (
            <div className="navigation">
                { items }
            </div>
            );
    }
});
React.render(<Navigation config={ navigationConfig } />, document.body);

Смотрите пример здесь– При наведении на "My Website" появится выпадающий список.

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

Теперь мы можем добавить много элементов вnavigationConfig среди них, и мы также можем добавить стили к функции выпадающего списка.Посмотреть пример。

Итог

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

Источник: http://www.oschina.net/translate/getting-started-with-react