将新服务器数据传递给react.js组件

时间:2013-12-16 07:13:45

标签: javascript reactjs

我是React.js的新手,并且正在努力理解几个核心概念,以决定我们是否应该将此库用于我们的应用程序。我的主要问题是实际处理从服务器获取的模型中的更新。

想象一下,我有一个应该显示五种不同模型的页面。我已经按照本文所述的方式构建了它:http://facebook.github.io/react/blog/2013/11/05/thinking-in-react.html,所以我有“root”组件,其中所有5个模型都通过并使用道具,它们将归结为包含此模型的组件。所以,现在更新了2个模型(我从我的模型代码中获取了这些事件,这些模型代码存在于反应组件之外),我需要在UI上反映这一点。做这个的最好方式是什么?

我正在考虑以下选项:

  1. 再次使用新数据运行renderComponent并依赖DOM差异反应技术。我对此感到担心,因为我需要在任何微小的数据更改上执行此操作。
  2. 为拥有此模型的组件调用setState。通过这种方式,数据变得不是道具,而是(根据我的理解)不是良好实践的状态。另外,我没有看到任何方法来获取根组件之外的子组件。
  3. 有多个renderComponent调用,所以这样我就可以访问任何这个组件的setProps。但后来我需要做一些模板工作(让页面上的所有容器都可用)并且它会杀死所有反应的想法。
  4. 让一个根组件包含向用户显示的应用程序中的所有可能模型,并调用setProps来更改模型。我关注的是,这个组件会变得非常大,并且在某些时候成为“意大利面”+来自第1点的担忧。
  5. 提前谢谢你,希望我能清楚地解释我的问题。

3 个答案:

答案 0 :(得分:5)

使用相同的组件再次调用renderComponent,但不同的数据等同于调用component.setProps()。因此要么将所有模型保持为最小公分母中的状态,要么在更改时再次调用setProps / renderComponent。

答案 1 :(得分:4)

如果将数据作为props传递给子组件,则只需在更高级别更新它,它将强制渲染到使用相同属性对象的所有组件。考虑这个简单的例子:

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组件上的属性,但由于您将相同的属性(或数据)对象传递给子项,因此它们会对它做出反应并更新它的shadow 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);

这是有效的,因为react使用属性的单向绑定。但是如果说你的孩子组件会更新它的属性,它就不会爬到它的父母身上。为此,您需要ReactLink add-on或使用像Backbone提供的pub / sub接口。

答案 2 :(得分:4)

目前,我知道至少有三种方法可以将新数据传递给组件:

  1. 重新渲染组件。不要担心这种方法的效率,因为React似乎很好地处理了这个问题。有很好的文章:Change And Its Detection In JavaScript FrameworksUpdating with React.render
  2. 使用PubSub允许组件在数据更改时得到通知(您可以在How to communicate between React components帖子中找到一些有用的示例)。
  3. 使用回调绑定(请参阅下面的三个jsfiddles)
  4. 对于第三种选择,我受到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组件依赖于DataService API),但它可以进一步抽象,并由应用程序开发人员决定他的组件将遵循的特定于应用程序的约定。例如,在jsfiddle.net/kb3gN/12015(halloDataStatic对象和halloDataDynamic回调)中查看第一个和第二个示例的混合

    注意:示例中使用的ListenersService遵循Observer Pattern,在许多情况下,模式本身比专业人员更有利。但除此之外,我想用这些例子展示的是有一种数据绑定方式与回调

    <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'));