5 практических примеров для изучения фреймворка React

http://tutorialzine.com/2014/07/5-practical-examples-for-learning-facebooks-react-framework/
  • Перевод
  • Tutorial
Перевод статьи «5 Practical Examples For Learning The React Framework», Martin Angelov

Вы вероятно слышали о популярном JavaScript фреймворке от Facebook – React. Он используется на многих популярных веб-сайтах, в том числе в Facebook и Instagram. В этой статье вы увидите 5 практических примеров, построенных при помощи React, которые помогут вам начать работать с этим фреймворком.

Что особого в фреймворке React?


React построен на концепции компонентов. Он отличается от таких фреймворков, как Angular или Ember, которые используют двухстороннюю привязку данных для обновления HTML страницы. На мой взгляд, React проще для изучения, чем Angular или Ember – он намного меньше и хорошо работает с jQuery и другими фреймворками. Он, к тому же, чрезвычайно быстр, так как использует виртуальный DOM и обновляет только измененные части страницы (обращение к DOM до сих пор является, самой медленной частью современных веб-приложений, поэтому данный фреймворк и получает преимущество в производительности оптимизируя его).

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

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


Чтобы использовать React, вам нужно подключить единственный JavaScript файл. Facebook любезно предоставляет нам CDN, на который вы можете напрямую сослаться:

<script src="http://fb.me/react-0.10.0.min.js"></script>

Это даст вам доступ к глобальному объекту React, который содержит множество полезных методов, некоторые из них вы можете видеть в наших примерах. Рекомендованный способ написания веб-приложений на React, это используя язык JSX. Это слегка измененная версия JavaScript, которая позволит вам писать React компоненты используя HTML-подобный синтаксис прямо в вашем коде. Этот код компилируется в JavaScript, перед тем как быть интерпретированным браузером. JSX совсем необязателен – вы можете использовать обычный JavaScript, если предпочитаете.

Но хватит трепаться, давайте увидим немного кода!

1. Таймер


Как я упоминал раньше, строительные блоки приложений react, это компоненты. Они создаются при помощи вызова React.createClass() передавая объект со свойствами и методами. Каждый компонент имеет состояние (объект с данными) и является ответственным за свое отображение – метод render() вызывается, когда изменяется состояние. Вот пример построения обычного таймера:

/** @jsx React.DOM */

// Создаем компонент вызовом React.createClass.

var TimerExample = React.createClass({

    getInitialState: function(){

        // Это выполняется перед функцией render. Возвращаемый объект 
        // присваивается в this.state, чтобы мы могли использовать его позже.

        return { elapsed: 0 };
    },

    componentDidMount: function(){

        // componentDidMount вызывается react'ом, когда компонент 
        // был отрисован на странице. Мы можем установить интервал здесь:

        this.timer = setInterval(this.tick, 50);
    },

    componentWillUnmount: function(){

        // Этот метод вызывается сразу после того, как компонент удален
        // со страницы и уничтожен. Мы можем удалить интервал здесь:

        clearInterval(this.timer);
    },

    tick: function(){

        // Эта функция вызывается каждые 50мс. Она обновляет 
        // счетчик затраченного времени. Вызов setState заставляет компонент перерисовываться

        this.setState({elapsed: new Date() - this.props.start});
    },

    render: function() {
        
        var elapsed = Math.round(this.state.elapsed / 100);

        // Это даст нам число с одной цифрой после запятой dot (xx.x):
        var seconds = (elapsed / 10).toFixed(1);    

        // Хоть мы и возвращаем целый <p> элемент, react разумно обновит
        // только измененные части, содержащие переменную seconds.

        return <p>This example was started <b>{seconds} seconds</b> ago.</p>;
    }
});


React.renderComponent(
    <TimerExample start={Date.now()} />,
    document.body
);
полный пример JSFiddle

При инициализации компонента, вы можете передавать атрибуты любыми JavaScript выражениями, поместив их в скобки {}. В этом примере мы поставляем начальную дату, которая будет использована при каждом вызове функции tick() для расчета затраченных секунд.

2. Меню навигации


Давайте посмотрим, как мы можем обрабатывать событие клика в React — построим меню навигации:

/** @jsx React.DOM */

var MenuExample = React.createClass({

    getInitialState: function(){
        return { focused: 0 };
    },

    clicked: function(index){

        // Обработчик клика обновит состояние
        // изменив индекс на сфокусированный элемент меню

        this.setState({focused: index});
    },

    render: function() {

        // Здесь мы читаем свойство items, которое было передано
        // атрибутом, при создании компонента

        var self = this;

        // Метод map пройдется по массиву элементов меню,
        // и возвратит массив с <li> элементами.

        return (
            <div>
                <ul>{ this.props.items.map(function(m, index){
        
                    var style = '';
        
                    if(self.state.focused == index){
                        style = 'focused';
                    }
        
                    // Обратите внимание на использование метода bind(). Он делает
                    // index доступным в функции clicked:
        
                    return <li className={style} onClick={self.clicked.bind(self, index)}>{m}</li>;
        
                }) }
                        
                </ul>
                
                <p>Selected: {this.props.items[this.state.focused]}</p>
            </div>
        );

    }
});

// Отображаем компонент меню на странице, передав ему массив с элементами

React.renderComponent(
    <MenuExample items={ ['Home', 'Services', 'About', 'Contact us'] } />,
    document.body
);
полный пример JSFiddle

Вы, вероятно, заметили в коде данного примера, что мы используем атрибуты, как className, которых, на самом деле не существует в HTML. Это из-за того, что когда мы возвращаем элемент , мы на самом деле не возвращаем HTML, а только объект компонента React.DOM.p. Вы можете узнать об этом больше по этой ссылке.

3. Мгновенный поиск


Мы уже неоднократно убеждались в том, что пользователи не любят ожидание. Вот как вы можете использовать React для построения мгновенного поиска. (Для сравнения, смотрите нашу версию с AngularJS).

/** @jsx React.DOM */

// Давайте создадим компонент мгновенного поиска

var SearchExample = React.createClass({

    getInitialState: function(){
        return { searchString: '' };
    },

    handleChange: function(e){

        // Если вы закомментируете данную строку, поле ввода не изменит свое значение.
        // Это потому, что в React'е, поле не может измениться независимо от свойства
        // которое было ему присвоено. В нашем случае, это this.state.searchString.

        this.setState({searchString:e.target.value});
    },

    render: function() {

        var libraries = this.props.items,
            searchString = this.state.searchString.trim().toLowerCase();


        if(searchString.length > 0){

            // Ищем. Фильтрируем резальтаты.

            libraries = libraries.filter(function(l){
                return l.name.toLowerCase().match( searchString );
            });

        }

        return <div>
                    <input type="text" value={this.state.searchString} onChange={this.handleChange} placeholder="Type here" />

                    <ul> 

                        { libraries.map(function(l){
                            return <li>{l.name} <a href={l.url}>{l.url}</a></li>
                        }) }

                    </ul>

                </div>;

    }
});

                                                                                                                                                             
var libraries = [

    { name: 'Backbone.js', url: 'http://documentcloud.github.io/backbone/'},
    { name: 'AngularJS', url: 'https://angularjs.org/'},
    { name: 'jQuery', url: 'http://jquery.com/'},
    { name: 'Prototype', url: 'http://www.prototypejs.org/'},
    { name: 'React', url: 'http://facebook.github.io/react/'},
    { name: 'Ember', url: 'http://emberjs.com/'},
    { name: 'Knockout.js', url: 'http://knockoutjs.com/'},
    { name: 'Dojo', url: 'http://dojotoolkit.org/'},
    { name: 'Mootools', url: 'http://mootools.net/'},
    { name: 'Underscore', url: 'http://documentcloud.github.io/underscore/'},
    { name: 'Lodash', url: 'http://lodash.com/'},
    { name: 'Moment', url: 'http://momentjs.com/'},
    { name: 'Express', url: 'http://expressjs.com/'},
    { name: 'Koa', url: 'http://koajs.com/'},

];

// Отображаем компонент SearchExample на странице

React.renderComponent(
    <SearchExample items={ libraries } />,
    document.body
);
полный пример JSFiddle

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

4. Форма заказа


Настоящая мощь React`а проявляется, когда вы сочетаете несколько компонентов. Это позволяет вам лучше структурировать ваш код и ввести разделение обязанностей. Это также делает ваш код более легко используемым между разными частями вашего приложения. Вот пример формы заказа, которая позволяет клиентам планировать их бюджет (смотрите Angular версию):

// Это более сложный пример, который использует два компоненты -
// форма выбора сервиса и индивидуальные сервисы внутри.


var ServiceChooser = React.createClass({

    getInitialState: function(){
        return { total: 0 };
    },

    addTotal: function( price ){
        this.setState( { total: this.state.total + price } );
    },

    render: function() {

        var self = this;

        var services = this.props.items.map(function(s){

            // Создадим новый экземпляр компонента Service для каждого элемента массива.
            // Заметьте, что мы передаем функцию self.addTotal function в компонент.

            return <Service name={s.name} price={s.price} active={s.active} addTotal={self.addTotal} />;
        });

        return <div>
                    <h1>Our services</h1>
                    
                    <div id="services">
                        {services}

                        <p id="total">Total <b>${this.state.total.toFixed(2)}</b></p>

                    </div>

                </div>;

    }
});


var Service = React.createClass({

    getInitialState: function(){
        return { active: false };
    },

    clickHandler: function (){

        var active = !this.state.active;

        this.setState({ active: active });
        
        // сообщаем ServiceChooser, вызывая метод addTotal
        this.props.addTotal( active ? this.props.price : -this.props.price );

    },

    render: function(){

        return  <p className={ this.state.active ? 'active' : '' } onClick={this.clickHandler}>
                    {this.props.name} <b>${this.props.price.toFixed(2)}</b>
                </p>;

    }

});


var services = [
    { name: 'Web Development', price: 300 },
    { name: 'Design', price: 400 },
    { name: 'Integration', price: 250 },
    { name: 'Training', price: 220 }
];


// Отображаем ServiceChooser и передаем ему массив сервисов

React.renderComponent(
    <ServiceChooser items={ services } />,
    document.body
);
полный пример JSFiddle

Первая проблема возникающая при сочетании нескольких компонентов, это как позволить им общаться между собой. Первый способ, это передавать необходимые данные атрибутами при их инициализации. Это работает только в случае общения компонентов «от родительского к дочернему». Для общения в другом направлении, вы можете передать один из методов родительского компонента атрибутом в дочерний компонент. Дочерний компонент может потом вызывать этот метод и сообщать родителю о событиях. Вы можете прочитать об этом здесь.

5. Приложение с изображениями на AJAX


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

/** @jsx React.DOM */


// В этом примере мы также имеем два компонента - изображение и список изображений
// Изображения получены из Instagram через AJAX.


var Picture = React.createClass({

    // Этот компонент не содержит никакого состояния - он просто преобразует
    // то что было передано атрибутами в изображение.

    clickHandler: function(){
        
        // Когда компонент кликнут, вызываем обработчик onClick, 
        // который был передан атрибутом при создании:

        this.props.onClick(this.props.ref);
    },

    render: function(){

        var cls = 'picture ' + (this.props.favorite ? 'favorite' : '');

        return (

            <div className={cls} onClick={this.clickHandler}>
                <img src={this.props.src} width="200" title={this.props.title} />
            </div>

        );

    }

});

var PictureList = React.createClass({

    getInitialState: function(){
        
        // Массив изображений будет передан по AJAX, а 
        // избранные когда, пользователь кликнет по изображению:
        
        return { pictures: [], favorites: [] };
    },

    componentDidMount: function(){
        
        // Когда компонент загружается,  отправляем jQuery AJAX запрос

        var self = this;

        // конечная точка API, для загрузки популярных изображений дня

        var url = 'https://api.instagram.com/v1/media/popular?client_id=' + this.props.apiKey + '&callback=?';

        $.getJSON(url, function(result){

            if(!result || !result.data || !result.data.length){
                return;
            }

            var pictures = result.data.map(function(p){

                return { 
                    id: p.id, 
                    url: p.link, 
                    src: p.images.low_resolution.url, 
                    title: p.caption ? p.caption.text : '', 
                    favorite: false 
                };

            });

            // Обновляем состояние компонента. Это вызовет render.
            // Заметьте, что это обновляет только свойство pictures
            // и не удаляет массив избранных.

            self.setState({ pictures: pictures });

        });

    },

    pictureClick: function(id){

        // id содержит ID кликнутого изображения.
        // Найдем в массиве pictures и добавим его в избранные

        var favorites = this.state.favorites,
            pictures = this.state.pictures;

        for(var i = 0; i < pictures.length; i++){

            // Находим айди в массиве изображений

            if(pictures[i].id == id) {                  

                if(pictures[i].favorite){
                    return this.favoriteClick(id);
                }

                // Добавляем изображение в массив избранных,
                // и отмечаем, как избранное:

                favorites.push(pictures[i]);
                pictures[i].favorite = true;

                break;
            }

        }

        // Обновляем состояние, вызывая перерисовку
        this.setState({pictures: pictures, favorites: favorites});

    },

    favoriteClick: function(id){

        // Находим изображение в списке избранных и удалаяем его 
        // После этого находим изображение в массиве всех изображений и отмечаем, как не-избранное.

        var favorites = this.state.favorites,
            pictures = this.state.pictures;


        for(var i = 0; i < favorites.length; i++){
            if(favorites[i].id == id) break;
        }

        // Удаляем из избранных
        favorites.splice(i, 1);


        for(i = 0; i < pictures.length; i++){
            if(pictures[i].id == id) {
                pictures[i].favorite = false;
                break;
            }
        }

        // Обновляем состояние и перерисовываем
        this.setState({pictures: pictures, favorites: favorites});

    },

    render: function() {

        var self = this;

        var pictures = this.state.pictures.map(function(p){
            return <Picture ref={p.id} src={p.src} title={p.title} favorite={p.favorite} onClick={self.pictureClick} />
        });

        if(!pictures.length){
            pictures = <p>Loading images..</p>;
        }

        var favorites = this.state.favorites.map(function(p){
            return <Picture ref={p.id} src={p.src} title={p.title} favorite={true} onClick={self.favoriteClick} />
        });

        if(!favorites.length){
            favorites = <p>Click an image to mark it as a favorite.</p>;
        }

        return (

            <div>
                <h1>Popular Instagram pics</h1>
                <div className="pictures"> {pictures} </div>
                    
                <h1>Your favorites</h1>
                <div className="favorites"> {favorites} </div>
            </div>

        );
    }
});


// Отрисовываем компонент PictureList и добавлем его в body.
// я использую API ключ для тестового Instagram приложения.  
// Пожалуйста, сгенерируйте и используйте свой собственный здесь http://instagram.com/developer/

React.renderComponent(
    <PictureList apiKey="642176ece1e7445e99244cec26f4de1f" />,
    document.body
);
полный пример JSFiddle

Заметьте, что в нашем примере используется один и тот же компонент Picture для отображения списка всех и избранных изображений. Такое повторное использование является одним из преимуществ для организации вашего кода в компоненты.

Что дальше?


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

Но есть еще много чего, что нужно изучить о react. Я предлагаю начать отсюда:

Поделиться публикацией
Комментарии 46
    +7
    Из пункта 2 посмотрел пример — это вообще не ангуляровский подход, Там нужен ng-repeat + filter внутри ng-repeat. А не какие то циклы вручную и.т.п.
    То есть я хотел сказать, что если сравниваете, то хотя бы ознакомтесь в предметом сравнения, а то получается как в анекдоте:

    Встречаются два еврея:

    — Слышал я «Битлз», не понравилось. Картавят, фальшивят… Что людям в них нравится?!

    — А где ты их слышал?

    — Да мне Мойша напел…


    По поводу reactjs: долгие годы прошли у программистов чтобы понять что «логика + отображение» в одном месте => тяжело поддерживать
    и react наступает на те же самые грабли, что вы об этом думаете?
      0
      *из пункта 3
        0
        По поводу reactjs: долгие годы прошли у программистов чтобы понять что «логика + отображение» в одном месте => тяжело поддерживать
        и react наступает на те же самые грабли, что вы об этом думаете?

        React это просто отображение состояния, а не бизнес-логика. В примерах описанных выше тоже всё не совсем по реакту написано.

        Если хотите узнать как действительно нужно работать с логикой, то почитайте про Flux.

        Я бы сказал что примеры сверху даже вредны, так как совершенно не раскрывают что же делать с данными, чтоб код не превратился в кашу.
          0
          Точно. Многие думают что JS — логика по определению, HTML — презентация. Но в случае с React — только JS используется для презентации, как более экспрессивный язык. JSX — всего лишь опциональная обертка, немного упрощающая работу. Смешивания нигде нет, React отображает, а логику можно (и нужно) располагать в другом месте. Конечно, оно может возникнуть в результате применения плохих практик, но так, в общем-то, в любом фреймворке.
            +2
            React это просто отображение состояния, а не бизнес-логика
            То есть это получается шаблонизатор, тем более я не понимаю зачем его с ангуляром сравнивать.

            Если хотите узнать как действительно нужно работать с логикой, то почитайте про Flux.
            Прочитал про flux, понял почему генерируют размётку из JS (чтобы более производительно рендерить, но это не сильно распростанённая проблема, текущие фреймворки вполне производительны). Те же представители фейсбука рассказывающие про тысячи комментариев на странице их динамически подгружают, то есть это любой фреймворк выведет без проблем с производительностью. Но всё же javascript не подходит для шаблонизации, его код легко поломать и для модификации требуется более высокооплачиваемый специалист, то есть «сделать кнопку овальной и с картинкой» кроссбраузерно, дешевле и быстрее верстальщику, нежели серьёзному фронтенд разработчику.

            И про работу слогикой:
            на мой взгляд flux ничего нового не привнёс в плане архитектуры,
            контроллеры назвали «action»,
            модели назвали «store»,
            а вьюшки остались «views», только в отличие от того же ангуляра, простейшие вещи в реакте делать нелегко.
              0
              То есть это получается шаблонизатор, тем более я не понимаю зачем его с ангуляром сравнивать.

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

              Что касается сложности вёрстки, так эта проблема надумана и существует в рамках конкретных кейсов, а не в общем случае.

              на мой взгляд flux ничего нового не привнёс в плане архитектуры,

              Flux это не что-то новое, а ответ на вашу реплику:
              «логика + отображение» в одном месте => тяжело поддерживать
              и react наступает на те же самые грабли
                0
                Вы сильно лукавите, когда говорите что это «получается шаблонизатор».
                Я не лукавлю, просто говорю о том как понимаю, если вы объясните или скажете где посмотреть что я понял неверно, я буду вам очень благодарен.
                Как самая близкая ассоциация с ангуляром — это его директивы в сравнении с reactjs, то есть компонент состоящий из контроллера + отображения?

                Что касается сложности вёрстки, так эта проблема надумана и существует в рамках конкретных кейсов, а не в общем случае.

                В джаваскрипт надо лазить чтобы вёрстку поправить (класс, например, добавить какому либо элементу или завраппить элемент)?
                  0
                  В джаваскрипт надо лазить чтобы вёрстку поправить (класс, например, добавить какому либо элементу или завраппить элемент)?

                  Смотря как у вас проект устроен. Я видел реализацию как в функцию render из reactjs импортировали шаблоны из других файлов. В этом случае всё выглядит так словно шаблоны лежат отдельно и написаны на близком к html языку (jsx).
                    0
                    К сожалению я не смогу правильно сравнить ангуляр и реакт, т.к. не очень хорошо знаю ангуляр.
                      0
                      Ну можно с директивами ангуляра и их взаимодействием поверхностно ознакомится в этих 2х видео за 10 минут:
                      https://egghead.io/lessons/angularjs-first-directive
                      https://egghead.io/lessons/angularjs-directive-to-directive-communication

                      P.S. Сайт, классный, кстати, рекомендую там и другие видео посмотреть, там не только про ангуляр. Про react тоже выложили 26 видео, я в течение недельки посмотрю, углублюсь.
                        0
                        Компоненты реакта, кстати, похожи на директивы ангуляра. Но в отличие от ангуляра, они не требуют даже двух минут видео на понимание :)

                        Например надо тебе абстрактный список как угодно отрисованных элементов. Возможно потом сортировку какую приделать к нему, или банальную рамку снаружи. Делаешь:
                        var myList = react.createClass({ 
                           render: function() { 
                              return dom.div({}, this.props.items.map(this.props.renderItem))
                           }
                        )
                        


                        и потом используешь в другом компоненте:
                        var anotherComponent = react.createClass({
                          render: function() {
                            return dom.div({}, myList({
                              items: [1,2,3], // передаем данные на отрисовку 
                              renderItem: function(item) { return dom.span({}, item.toString()); } // передаем функцию отрисовки одного элемента
                            }))
                          }
                        })
                        
                          +1
                          Плохой пример, потому что в ангуляре это уже написано (и сортировка тоже). И повторное использование в директивах там понятнее.
                          По аналогии с вашим примером, для добавления рамки снаружи я просто в вёрстке перенесу ng-repeat на уровень выше.
                          И будет выглядеть вместо:
                          <div>
                             <span ng-repeat="item in items">{{item}}</span>
                          </div>
                          

                          Вот так:
                          <div>
                            <div ng-repeat="item in items"> 
                              <span>{{item}}</span>
                            </div>
                          </div>
                          


                          А если нужен просто обрамляющий div я его добавлю не думая о директивах и angularjs:
                          <div>
                             <div>
                               <span ng-repeat="item in items">{{item}}</span>
                             </div>
                          </div>
                          

                          И это сможет сделать самостоятельно верстальщик, без понимания что там за «классы React» и «неоднозначного синтаксиса javascript с его особенностями».

                          P.S. вообще сравните как выглядит ваш пример и то что я написал.
                          P.P.S. мало того для такой простой задачи в ангуляре вообще не нужны никакие самостоятельно поддерживаемые js файлы, что есть большой плюс.
                            0
                            Т.е. ты мне привел пример одноразового использования, а я — полноценного компонента.

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

                            Тебе придется делать директиву. А это уже и JS-код, и шаблон где-то в другом файле. И нужно во всех местах где используется поменять ng-repeat на свою директиву.

                            И да, я согласен что html с директивами, в относительно простых случаях более понятен, особенно верстальщикам. Но если верстальщих и фронтенд-разработчик — одно лицо, а логика во view хитрая — реакт выигрывает.
                              0
                              Напишу своё видение React-а.

                              React позволяет не думать о `точечной` перерисовки компонентов.
                              setState на root-овой компоненте, можно сравнить с перезагрузкой страницы,
                              только в случае React-а это делается без перезагрузки и обновляются только те узлы, которые изменились.

                              Вот, упрощённый, пример о чем я говорю:

                              Window = React.createClass
                              reload: (cb)-> @forceUpdate(cb) 
                              render: ->
                              @transferPropsTo(Document())
                              
                              Document = React.createClass
                              render: -> @transferPropsTo(Html)
                              
                              Html = React.createClass
                              render: -> React.DOM.div(null, Head, Body)
                              
                              
                              # etc.
                              
                              window = React.renderComponent(new Window({global: this}), document.body);
                              
                              # now you write your business logic
                              # and when model changed you simple reload all page
                              
                              user.on('change', window.reload);
                              


                              Такой подход, когда вам не надо хранить состояние представления,
                              позволяет вам писать в функциональном стиле.

                              React можно использовать с Angular, Ember и др. framework-ами. React решает
                              конкретную проблему — синхронизация состояния приложения с представлением, и вроде справляется с этим
                              не плохо.

                              Есть проблемы с инфраструктурой jshint, подсветка, completion и др., но решаемые так или иначе.

                              Сам активно изучаю React.
                              React довольно низкоуровневый, что может быстро привести к не читаемому коду.
                              Количество Best/Bads Practice не так много, поэтому приходится набивать `шишки`.
                              Для себя завел небольшой gist где собираю все, что показалось интересным, по React-у. Возможно найдете что-то полезное.
              +1
              (Для сравнения, смотрите нашу версию с AngularJS).
              Там нужен ng-repeat + filter внутри ng-repeat.

              Для сравнения сделал версию на AngularLight (на AngularJS будет так же).
              Хотя не понятно что там сравнивать…
                0
                Так и есть, просто там фильтр полноценный дополнительный(даже не фильтрующую функцию) в примере по ссылке написали и получилось не менее громоздко как в примере с reactjs.
                +1
                >По поводу reactjs: долгие годы прошли у программистов чтобы понять что «логика + отображение» в одном месте => тяжело поддерживать
                и react наступает на те же самые грабли, что вы об этом думаете?

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

                Но когда UI-логика действительно сложная — проще верстать тому же программисту, который пишет фронтенд-логику. И вот тогда получается что программисты, умеющие писать код на гибких и мощных языках, вынуждены писать на шаблонизаторе, где есть только if и foreach. Причем часто — чисто из религиозных побуждений.

                React ушел от идеи «тупого шаблонизатора», и через это пришел весь его кайф. Например можно передавать функцию для рендера ячейки внутрь компонента таблицы. Передавать один компонент как параметр в другой. Или функцию, которая вернет class для строки по данным. Или там хелперы делать какие угодно. Этот весь код можно рефакторить, можно писать на typescript и иметь всякий автокомплит и compile-time контроль типов. Можно к нему писать тесты. И так далее.

                И второй момент — уходит необходимость в т.н. view model. В случае с тупыми шаблонизаторами, между «контроллером» и «вьюхой» требуется еще одна прослойка, в которую умный «контроллер» положит все что не может расчитать глупый шаблонизатор. В react это не требуется — компонент может совершенно непринужденно обрабатывать входные данные.
                  0
                  И второй момент — уходит необходимость в т.н. view model. В случае с тупыми шаблонизаторами, между «контроллером» и «вьюхой» требуется еще одна прослойка, в которую умный «контроллер» положит все что не может расчитать глупый шаблонизатор. В react это не требуется — компонент может совершенно непринужденно обрабатывать входные данные.
                  На самом деле и в react эта прослойка тоже будет, потому что для отображения данных в компоненте их нужно будет, как минимум, получить извне. Опять же в статье его сравнивают с angularjs, а у него такой проблемы нет, ибо у него есть директивы которые умеют всё что умеет react, даже больше.

                  Проблема React в том, что для дополнительной производительности(которая неоправдана даже в видео товарищей из facebook, то есть всё что они там делают можно делать на любом фреймворке и будет быстро работать) добавили дополнительный слой абстракции между JS и вёрсткой и эта прослойка более неприятная чем переменные во вьюшках, потому что уже нельзя просто открыть файл и поправить вёрстку, теперь надо открыть файл в IDE, найти где лежит нужный класс, понять как, что и откуда наследуется и выводиться и потом уже поправить, когда есть много маленьких исправлений, это очень неприятный процесс. Особенно потому что первоначально смотришь на вёрстку, а править надо некоторые JS-абстракции.
                    0
                    >На самом деле и в react эта прослойка тоже будет

                    ViewModel все равно нужно, да. Но в нем не нужны поля вроде пред-вычислений, которые сложно сделать непосредственно во view. Типа сходить в словарик за значением по ключу, сделать if на пять вариантов, сгруппировать элементы массива — все это делается прямо в компоненте. Т.е. модель на входе react может быть более легкая и близкая к предметной области.

                    >Проблема React в том, что для дополнительной производительности… добавили дополнительный слой абстракции

                    Реакт наоборот убирает слой абстракции — который про DSL а-ля html с директивами, который переводит JS-объекты в DOM.

                    >… теперь надо открыть файл в IDE, найти где лежит нужный класс…

                    Дополнительный уровень абстракции — это всегда палка о двух концах. Оно в простых случаях добавляет геммороя, но на больших объемах и большей сложности кода отбивается за счет большего реюза, всяких рефакторингов, линтерах, find usages, и т.п.
                +1
                Я понимаю, что это перевод, но как можно называть библиотеку фреймворком? В документации все четко расписано:
                React is a JavaScript library for creating user interfaces by Facebook and Instagram. Many people choose to think of React as the V in MVC.

                Поэтому сравнивать его с Angular.js и Ember.js уже не верно. Это все равно, что написать, что jQuery как фреймворк проще, чем Angular.js и Ember.js :)
                  –2
                  Да, ставить React наряду с angular немного не верно, но тем не менее, большинство называет его фреймворком.
                    +2
                    Ну дак может уже пора называть его правильно?
                      0
                      Если большинство называет Java как «Ява», то это значит, что Jazz теперь называется «Язь» :)

                      P.S. Большинство, которое не читает официальную документацию?

                        +1
                        Блин, народ, я переводчик, поэтому перевожу как написано. Может мне вообще React framework переводить, как библиотека Реагирования? За что минусы то? Я согласен, что это скорее библиотека, но я не могу переводить, как мне вздумается.
                          0
                          Но если статья не верная, то зачем её переводить? Возмите за основу оригинал и переработайте. Мы вроде тут оцениваем не качество перевода, а ценность материала статьи.
                            –1
                            Статья была рекомендована в списке полезностей и интересностей для веб-разработчика, вот и перевел. Я бы не сказал, что эта статья плохая, она для тех, кто хочет ознакомиться с основами react. Это, кстати, подтверждает рейтинг статьи.
                              0
                              И кстати, скоро переведу более углубленный цикл статей по React, это будет уже для ознакомившихся читателей. Всему свое время;)
                            +2
                            Мне кажется, что в таких случаях стоит в начале статьи делать вступление от переводчика, в котором бы оговаривались такие моменты.
                              0
                              Вы часто такое в переводах видите?
                                +4
                                Реже, чем хотелось бы.
                      +6
                      render: function() {
                      
                              var self = this;
                      
                              return (
                                  <div>
                                      <ul>{ this.props.items.map(function(m, index){
                              
                                          var style = '';
                              
                                          if(self.state.focused == index){
                                              style = 'focused';
                                          }
                      
                                          return <li className={style} onClick={self.clicked.bind(self, index)}>{m}</li>;
                              
                                      }) }
                      


                      image

                      Мне кажется они переборщили с перемешиванием js и html.
                        0
                        Да, примеры, мягко говоря, не ахти.
                        Перемешано, все что только можно перемешать.
                          0
                          Там можно писать на JS, я так и делаю. Данный пример на typescript и со штатным хелпером cx для слепливания class-ов, я бы написал как-то так:
                          var items = this.props.items.map((text, index) => 
                               dom.li({ 
                                  className: cx({ focused: self.state.focused == index }),
                                  onClick: this.clicked.bind(self, index)
                               }, text)
                          )
                          return dom.div({}, dom.ul({}, items))
                          
                          +1
                          А за счёт чего работает setInterval(this.tick, 50); — кто обеспечивает передачу корректного this при вызове функции tick()? React/JSX что, подменяют window.setInterval или форсируют Function.prototype.bind() для всех методов создаваемого класса/компонента?
                            +1
                            Похоже, что так. React automatically binds component methods to each instance so there’s no need to worry about maintaining scope — выдержка из статьи, которую я как-раз перевожу.
                              0
                              При инстанцировании класса компоненты все методы прибиндиваются.
                              +1
                              Кода нужно гораздо меньше, если писать на LiveScript:
                              red-badger.com/blog/2014/05/27/using-livescript-with-react/

                              Одновременно с этим отпадает надобность использовать JSX, и получается интересный, JADE-подобный синтаксис, который легко читается:

                              product-row = React.create-class do
                                render: ->
                                  tr null,
                                    td null,
                                      span do
                                        if @props.product.stocked
                                          style:
                                            color: 'red'
                                        @props.product.name
                                    td null, @props.product.price
                              
                                0
                                Да, напрашивается там безскобочный синтаксис на отступах — тот же coffeescript должен тоже норм зайти. А то в JS слишком много скобок выходит. Впрочем, я пишу на typescript — в целом нормально получается, там как минимум есть лямбды с замыканием this, без них тоже печально.
                                –1
                                Товарищи! Пожалуйста, будьте грамотными. Не пишите, как ПТУшники или курицы с лапами
                                Автор перевода!..
                                  0
                                  Что конкретно вам не нравится? Такой же комментарий можно написать под любым постом, а вы пожалуйста обоснуйте, и подскажите, что не так, чтобы я мог исправить.
                                    0
                                    Навскидку
                                    Вы можете заключить любое JavaScript выражение между скобок {}, при создании компонента
                                    Мы, снова и снова, узнаем, что люди терпеть не могут ожидание.
                                      +1
                                      Спасибо, исправил. Дело в том, что при чтении своего перевода некоторые предложения воспринимаются на автоматизме, даже если они очень корявые. В таком случае помогает взгляд со стороны.
                                  0
                                  Остался один вопрос, а что если нужно будет рендерить не в конкретное место на HTML странице, а во множество?
                                  Например, если есть HTML страница, в которой в разных частях находятся баннеры, например, в элементе <AdvBox />.
                                  Манипуляции с document.querySelectorAll в React.renderComponent не дали положительного результата, а если оставить document.body, то перезаписывается весь body полностью.
                                    0
                                    есть решение?
                                    0
                                    Примеры в jsfiddle больше не работают, или я что-то не так делаю? При открытии их на вкладке "результат" просто пустая страница.

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

                                    Самое читаемое