我是React.js的新手,并且很难理解几个核心概念来决定我们应该将这个库用于我们的应用程序.我的主要问题是实际处理从服务器获取的模型中的更新.
想象一下,我有一个应该显示五种不同模型的页面.我已经按照本文所述的方式构建了它:http://facebook.github.io/react/blog/2013/11/05/thinking-in-react.html,所以我有"root"组件所有5个模型通过并使用道具,他们将下载到持有此模型的组件.所以,现在更新了2个模型(我从我的模型代码中获取了这些事件,这些模型代码存在于反应组件之外),我需要在UI上反映这一点.做这个的最好方式是什么?
我正在考虑以下选项:
再次使用新数据运行renderComponent并依赖DOM diff反应技术.我对此感到担心,因为我需要在任何微小的数据更改上执行此操作.
为包含此模型的组件调用setState.通过这种方式,数据变得不是道具,而是(根据我的理解)不是良好实践的状态.另外,我没有看到任何方法来获取根组件之外的子组件.
有多个renderComponent调用,所以这样我就可以访问任何这个组件的setProps.但后来我需要做一些模板工作(让页面上的所有容器都可用)并且它会杀死所有反应的想法.
让一个根组件包含向用户显示的应用程序中的所有可能模型,并调用setProps来更改模型.我关注的是,这个组件将会变得非常大,并且在某些方面成为"意大利面"+点1的关注点.
提前谢谢你,希望我能清楚地解释我的问题.
目前,我至少知道三种将新数据传递到组件的方法:
重新渲染组件。不用担心这种方法的效率,因为React似乎可以很好地处理这个问题。关于这一点,有很多不错的文章:JavaScript框架中的更改及其检测以及React.render的更新
使用PubSub允许组件在数据更改时得到通知(一些有用的示例,您可以在“ 如何在React组件之间进行通信”一文中找到)。
用回调绑定(请参阅下面的三个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);
此实现未完全遵循隔离组件的概念(因为Hello组件依赖于DataService API),但是可以进一步抽象,并且取决于应用程序开发人员其组件将遵循哪些特定于应用程序的约定。例如,请参见jsfiddle.net/kb3gN/12015中第一个示例和第二个示例的混合(halloDataStatic对象和halloDataDynamic回调)
注意:示例中使用的ListenersService遵循观察者模式,并且在许多情况下,模式本身比利弊多。但是除此之外,我想通过这些示例展示的是,有一种方法可以将数据与回调绑定
<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'));
使用相同的组件再次调用renderComponent但不同的数据等同于调用component.setProps().因此要么将所有模型保持为最小公分母中的状态,要么在更改时再次调用setProps/renderComponent.
如果将数据作为道具传递到子组件,则可以简单地在更高级别进行更新,这将强制向使用相同属性对象的所有组件进行渲染。考虑以下简单示例:
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);
之所以可行,是因为react使用属性的单向绑定。但是,如果说您的子组件将更新其属性,则它不会爬升到其父组件。为此,您将需要ReactLink插件或使用一个Backbone提供的pub / sub接口。