2013-12-16 41 views
20

我是React.js的新手,并且很难理解几个核心概念,以决定是否应该在我们的应用程序中使用此库。我的主要问题实际上是处理从服务器获取的模型中的更新。将新的服务器数据传递给react.js组件

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

我在考虑下列选项:

  1. 运行renderComponent用新数据再次,依靠DOM DIFF反应技术。我担心这一点,因为我需要对数据进行任何小小的更改。
  2. 为持有此模型的组件调用setState。通过这种方式,数据变得不是道具,而是说明(根据我的理解)并不是一个好习惯。另外,我没有看到任何方式来获取根组件之外的子组件。
  3. 有多个renderComponent调用,所以这样我就可以访问任何这个组件的setProps。但是接下来我需要做一些模板工作(让页面上的所有容器都可用),并且它会杀死所有的反应思想。
  4. 拥有一个根组件,其中包括向用户显示的应用程序中的所有可能模型以及用于更改模型的setProps。在这里我所关注的,该组件将增长相当大,由点1

成为“意大利面条”在某个时刻关注+预先感谢您,并希望我能解释清楚我的问题。

+1

您的模型层是否使用事件?如果是这样,你可以使用它们来触发渲染。 – krs

+0

我已经添加了一个类似的问题关于使用React的骨干模型,以及当模型更新时应如何更新视图http://stackoverflow.com/questions/20371566/handling-backbone-model-collection-changes-in-react -js –

+1

您是否找到解决方案? – copndz

回答

4

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

+0

您是否只有一个通过renderComponent创建的根组件?或者你有一个单独的组件,可以为应用程序的每个逻辑部分呈现不同的容器?感谢您的回复,并试图帮助我! –

4

如果将数据作为道具传递给子组件,则可以在更高级别上更新它,并强制渲染使用相同属性对象的所有组件。考虑一个简单的例子:

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 add-on或使用像Backbone提供的pub/sub接口。

+3

你从React里面做的所有事情,我想我想要的是从外部reactjs中“刷新”dom。 – copndz

+0

是的,我也不理解单向概念,这里是一个射击绑定,一旦完成,即使我改变了外部模型中的数据,这在渲染类中也没有效果:this.props.model.modelproperty没有检查更新 –

+2

您不应该更改道具,您需要通过调用setState来反映数据的任何更改,只有这样才会触发受影响组件和子组件的重新渲染。 – markus

4

目前我知道至少有三种方式将新数据传递给一个组件:

  1. 重新渲染组件。不要担心这种方法的效率,因为React似乎处理得很好。有关于此的好文章:Change And Its Detection In JavaScript FrameworksUpdating with React.render
  2. 使用PubSub允许在数据更改时通知组件(在How to communicate between React components文章中可找到一些有用的示例)。
  3. 有回调绑定(见下面的第三jsfiddles)

对于我被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); 

此实现不完全以下隔离组件(因为你好组件依赖于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')); 
相关问题