Подтвердить что ты не робот

Передача новых данных сервера компонентам react.js

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

Представьте себе, что у меня есть страница, которая должна отображать пять разных моделей. Я построил его так, как описано в этой статье: http://facebook.github.io/react/blog/2013/11/05/thinking-in-react.html, поэтому у меня есть "корневой" компонент, в котором все 5 моделей прошли и используя реквизиты, они сводятся к компонентам, которые поддерживают эти модели. Итак, теперь обновлено 2 модели (я получаю эти события из своего кода модели, которые живут вне реагирующих компонентов), и мне нужно отразить это в пользовательском интерфейсе. Каков наилучший способ сделать это?

Я думаю о следующих вариантах:

  • Запустите renderComponent с новыми данными еще раз и опирайтесь на методы реагирования DOM diff. Я беспокоюсь об этом, так как мне нужно будет сделать это при любом небольшом изменении данных.
  • Вызов setState для компонентов, поддерживающих эти модели. Таким образом, данные становятся не опорой, но состояние, которое (из того, что я понимаю), не является хорошей практикой. Кроме того, я не вижу способа получить ссылку на дочерний компонент вне корневого компонента.
  • Имея несколько вызовов renderComponent, таким образом, у меня будет доступ к setProps для любого из этого компонента. Но тогда мне нужно будет сделать некоторые работы по шаблонам (чтобы все контейнеры были доступны на странице), и он убивает всю реакционную идею.
  • Наличие одного корневого компонента, включающего все возможные модели в приложении, отображаемого для пользователей, и вызов setProps для изменения моделей. Меня беспокоит, что этот компонент будет расти довольно крупным и стать "спагетти" в какой-то момент + опасениями из точки 1.

Спасибо заранее и надеюсь, что я смог четко объяснить свою проблему.

4b9b3361

Ответ 1

Вызов renderComponent снова с тем же компонентом, но разные данные эквивалентны вызову component.setProps(). Поэтому либо сохраняйте все модели как состояние в наименее общем знаменателе, либо просто вызовите setProps/renderComponent снова, когда он изменится.

Ответ 2

Если вы передаете данные в виде реквизита до вашего дочернего компонента, вы можете просто обновить его на более высоком уровне, и это заставит рендер ко всем компонентам, которые используют один и тот же объект свойства. Рассмотрим этот простой пример:

var World = React.createClass({
    render: function() {
        return <strong>{this.props.name}</strong>;
    }
});

var Hello = React.createClass({
    clickHandler: function() {
        this.setProps({ name: 'earth' });
    },
    render: function() {
        return (
            <div>
                Hello <World name={this.props.name} />
                <button onClick={this.clickHandler}>Click me</button>
            </div>
        );
    }
});

Теперь, когда пользователь нажимает кнопку, вы меняете свойство на компоненте Hello, но поскольку вы передали объект с тем же свойством (или данным) для детей, они будут реагировать на него и соответствующим образом обновлять свой теневой DOM.

Вот сценарий, что я имею в виду: http://jsfiddle.net/xkCKR/

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

// simple example of a data model
var Data = { name: 'world' };

var World = React.createClass({
    render: function() {
        return <strong>{this.props.data.name}</strong>;
    }
});

var Hello = React.createClass({
    clickHandler: function() {
        this.setProps({
            data: { name: 'earth' }
        });
    },
    render: function() {
        return (
            <div>
                Hello <World data={this.props.data} />
                <button onClick={this.clickHandler}>Click me</button>
            </div>
        );
    }
});

React.renderComponent(<Hello data={Data} />, document.body);

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

Ответ 3

В настоящее время я знаю как минимум три способа передачи новых данных в компонент:

Для третьего варианта меня вдохновил ответ StevenH и немного расширил его. Пожалуйста, проверьте мою реализацию в j sfiddle.net/kb3gN/12002/.

var Data = { value: 1 };

var dataChange = function(callback){
    if(callback){
        callback(Data);
        setInterval(function(){
            Data.value++;
            callback(Data);
        }, 1000);
    }
    return Data;
};

var World = React.createClass({
    render: function() {
        return <strong>{this.props.data.value}</strong>;
    }
});

var Hello = React.createClass({
    getInitialState: function() {
        return {
          data: this.props.dataChange()
        };
    },
    componentDidMount: function() {
        this.props.dataChange(this.updateHandler)
    },
    updateHandler: function(data) {
        this.setState({
          data: data
        });
    },
    render: function() {
        return (
            <div>
                Value: <World data={this.state.data} />
            </div>
        );
    }
});

React.renderComponent(<Hello dataChange={dataChange} />, document.body);

Также есть расширенная версия jsfiddle.net/kb3gN/12007.

function ListenersService(){
    var listeners = {};
    this.addListener = function(callback){
        var id;
        if(typeof callback === 'function'){
            id = Math.random().toString(36).slice(2);
            listeners[id] = callback;
        }
        return id;
    }
    this.removeListener = function( id){
        if(listeners[id]){
            delete listeners[id];
            return true;
        }
        return false;
    }
    this.notifyListeners = function(data){
        for (var id in listeners) {
          if(listeners.hasOwnProperty(id)){
            listeners[id](data);
          }
        }
    }
}

function DataService(ListenersService){
    var Data = { value: 1 };
    var self = this;

    var listenersService = new ListenersService();
    this.addListener = listenersService.addListener;
    this.removeListener = listenersService.removeListener;
    this.getData = function(){
        return Data;
    }

    setInterval(function(){
        Data.value++;
        listenersService.notifyListeners(Data);
    }, 1000);
}
var dataSevice = new DataService(ListenersService);

var World = React.createClass({
    render: function() {
        return <strong>{this.props.data.value}</strong>;
    }
});

var Hello = React.createClass({
    getInitialState: function() {
        return {
          data: this.props.dataService.getData()
        };
    },
    componentDidMount: function() {
        this.props.dataService.addListener(this.updateHandler)
    },
    updateHandler: function(data) {
        this.setState({
          data: data
        });
    },
    render: function() {
        return (
            <div>
                Value: <World data={this.state.data} />
            </div>
        );
    }
});

React.renderComponent(<Hello dataService={dataSevice} />, document.body);

Эта реализация не полностью соответствует идее изолированных компонентов (поскольку компонент Hello зависит от API DataService), но он может быть абстрагирован далее и зависит от разработчика приложения, для которого будут применяться приложения, соответствующие его компонентам. Например, см. Сочетание первого и второго примеров в jsfiddle.net/kb3gN/12015 (объект halloDataStatic и обратный вызов halloDataDynamic)

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

<div id="static"></div>
<div id="dynamic"></div>
<script>

function ListenersService(){
    var listeners = {};
    this.addListener = function(callback){
        var id;
        if(typeof callback === 'function'){
            id = Math.random().toString(36).slice(2);
            listeners[id] = callback;
        }
        return id;
    }
    this.removeListener = function( id){
        if(listeners[id]){
            delete listeners[id];
            return true;
        }
        return false;
    }
    this.notifyListeners = function(data){
        for (var id in listeners) {
          if(listeners.hasOwnProperty(id)){
            listeners[id](data);
          }
        }
    }
}

function DataService(ListenersService){
    var Data = { value: 1 };
    var self = this;

    var listenersService = new ListenersService();
    this.addListener = listenersService.addListener;
    this.removeListener = listenersService.removeListener;
    this.getData = function(){
        return Data;
    }

    setInterval(function(){
        Data.value++;
        listenersService.notifyListeners(Data);
    }, 100);
}
var dataSevice = new DataService(ListenersService);
var halloDataDynamic = function(callback){
    var data = dataSevice.getData();
    if(callback){
        dataSevice.addListener(function(data){
            callback(data);
        });
    }
    return data;
};
var halloDataStatic = dataSevice.getData();

var World = React.createClass({
    render: function() {
        return <strong>{this.props.data.value}</strong>;
    }
});

var Hello = React.createClass({
    getInitialState: function() {
        var data;
        if(typeof this.props.halloData === 'function'){
            data = this.props.halloData(this.updateHandler)
        }
        else data = this.props.halloData;
        return {
          data: data
        };
    },
    updateHandler: function(data) {
        this.setState({
          data: data
        });
    },
    render: function() {
        return (
            <div>
                Value {this.props.name}: <World data={this.state.data} />
            </div>
        );
    }
});
</script>

React.renderComponent(<Hello halloData={halloDataStatic} name="static"/>, document.getElementById('static'));
React.renderComponent(<Hello halloData={halloDataDynamic} name="dynamic"/>, document.getElementById('dynamic'));