对于一些应用(尤其是具有简单的数据流),双向绑定是非常高校的。而对于更加复杂的应用,数据双向绑定已被证明是非常低效的。对于实际的UI设计来说,俨然成为一种障碍。React不能解决应用数据流的大量问题(虽然Flux可以),但是基于它的一个组件可以。
有了这个组件,React可以处理数据流问题以及显示UI更新的结果。UI更新方面的问题是通过一种名为Reconciliation的模块解决的。这个模块涉及到一种虚拟DOM的新思路。在下篇文章中,我们会详细讲解Reconciliation。这篇文章我们主要关注数据流问题和React组件间使用的各种数据问题。
各种组件数据
React组件中数据是以属性或者状态的形式存储的。
属性相当于组件的输入值。他们通常用在组件的渲染和状态初始化的过程中。当组件实例化后,属性就应该确认不变了。属性值只有在实例化组件时才能被设置。这时,当组件再次在DOM中渲染时,React将会对新旧属性值做比较,以此来确定哪些DOM需要更新。
现在看一个设置属性值引起DOM更新的例子。
https://codepen.io/SitePoint/pen/XKqQqN
状态数据的变化一般是通过绑定组件的事件方法实现的。通常,更新状态会引起React组件的重新渲染。一个组件初始化之前,它的状态必须首先初始化。已经初始化的值包含常量值和属性值(上文提过的)。
对于像Angular.js之类的框架来说,属性是单向绑定的数据,状态是双向绑定的数据。这不是一个完美的比喻方式。Angular.js 使用一种具备两种使用方式的数据对象而React使用两种类型的数据对象,彼此都有特定的使用方式。
属性
我的以前React的文章(https://www.sitepoint.com/getting-started-react/)讲过指定和获取属性的语法。这篇文章通过多种代码范例讲解了Javascript和JSX中的静态属性和动态属性的使用方法。以这篇文章为基础,让我们看一些有趣的与属性操作相关的细节。
当需要给组件添加一个CSS的class属性时,我们必须用className代替class。React要求这个是因为ES2015将class作为定义对象的关键词。为了避免冲突,React将className作为替代品。如果使用了class作为属性名,React将会给出相关提示以此提醒开发者用className代替class。
注意观察下方不正确的class属性名,微软Edge浏览器会给出有用的警告信息。
当把class改为classname,警告信息随之消失。
当把class改为className,警告信息随之消失。查看下面来自CodeOpen的完整范例。
https://codepen.io/SitePoint/pen/akGxGW
除了像className这样的属性名称,React属性还有其他有趣的地方。比如,动态组件属性就是一个反模式。动态属性能够在组建实例化过程中被设置,但是组件实例化后动态属性就不能变化了。这个过程包括组件的实例化和渲染。一个组件的动态值可以被称为状态,它是通过state属性控制的而不是props属性。
在下面的实例代码中,通过createElement实例化一个名字为SomeComponent的组件,并给它添加了两个属性。
JavaScript:
var someComponent = React.createElement(SomeComponent);
someComponent.props.prop1 = "some value";
someComponent.props.prop2 = "some value";
JSX:
var someComponent = <SomeComponent />;
someComponent.props.prop1 = "some value";
someComponent.props.prop2 = "some value";
操作已经实例化的组件的props能够产生很难追踪的错误。当然,改变属性也不能引起组件的更新,因为属性已经与其组件不同步了。
因此,我们应该在组件的实例化过程中设置属性。代码如下:
JavaScript:
var someComponent = React.createElement(SomeComponent, {
prop1: "some value",
prop2: "some value"
});
JSX:
var someComponent = <SomeComponent prop1="some value" prop2="some value" />
然后,组件可以重新渲染,在这点上,React将执行核对过程,以确定新的属性值如何影响DOM。然后,DOM随着更改而更新。
有关DOM更新的例子,可以查看文章顶部第一个CodePen的例子。
状态
状态表示由组件更改的数据,通常通过与用户的交互来实现。为了方便这种更改,事件处理程序被注册为适当的DOM元素。当事件发生时,更新的值将从DOM检索,并通知新状态的组件。在组件可以利用状态之前,状态必须通过函数getinitialstate初始化。通常情况下,函数getinitialstate使用属性或者其他方式传过来的静态值初始化状态。
var Message = React.createClass({
getInitialState: function() {
return { message: this.props.message };
},
初始化状态后,状态值可以像渲染组件时的属性值一样使用。为了捕获更新状态的用户交互,事件处理程序已经注册。为了保持React组件的独立性,事件处理函数对象可以作为属性传递,或者直接在组件对象本身上定义。
https://codepen.io/SitePoint/pen/JKvVvy
React的好处之一是使用标准的HTML事件。标准的HTML事件包含的是标准的HTML事件对象。不需要学习特殊事件库、事件处理程序或自定义事件对象。由于现代浏览器基本上兼容,所以不需要中间的跨浏览器库,如jQuery。
为了处理状态的变化,需要使用setstate函数在适当的状态属性下设置新值。调用此函数将使组件重新渲染。
如下图所示在Visual Studio代码编辑器,使用setstate函数是从_messagechange事件处理程序中调用。
总结
React组件提供了两种处理数据的机制:属性和状态。在不可变属性和可变状态之间划分数据更清楚地标识每种数据的作用,以及组件与它的关系。一般来说,属性是首选的,因为它们简化了数据流。状态对于捕获用户交互和其他UI事件产生的数据更新非常有用。
属性和状态之间的关系促进了数据通过组件的流动。当实例化和渲染组件时,属性可以用来初始化状态,状态值可以用来设置属性。来自用户交互的新值通过状态捕获,然后用于更新属性。
应用程序中较大的数据流通过一个称为Flux的模式完成。
本文为翻译作品,原文来自sitepoint,作者Eric Greene。原文地址:https://www.sitepoint.com/working-with-data-in-react-properties-state/