Components
React提供了和以往不一样的方式来看待视图,它以组件开发为基础。组件是React的核心概念,React 允许将代码封装成组件(component),然后像插入普通 HTML 标签一样,在网页中插入这个组件。React.createClass 方法就用于生成一个组件类。对React应用而言,你需要分割你的页面,使其成为一个个的组件。也就是说,你的应用是由这些组件组合而成的。你可以通过分割组件的方式去开发复杂的页面或某个功能区块,并且组件是可以被复用的。这个过程大概类似于用乐高积木去瓶装不同的物体。我们称这种编程方式称为组件驱动开发。
Declaration:组件声明
React是可以同时渲染HTML标签与组件的,但是要注意的是,一般Tags元素都是小写开头,而Component都是以大写字母开头,以下面为例:
var myDivElement = <div className="foo" />;
React.render(myDivElement, document.getElementById('example'));
而如果需要渲染一个Component:
var MyComponent = React.createClass({/*...*/});
var myElement = <MyComponent someProperty={true} />;
React.render(myElement, document.getElementById('example'));
LifeCycle
组件的生命周期分成三个状态:
Mounting:已插入真实 DOM,即Initial Render
Updating:正在被重新渲染,即Props与State改变
Unmounting:已移出真实 DOM,即Component Unmount
React 为每个状态都提供了两种处理函数,will 函数在进入状态之前调用,did 函数在进入状态之后调用,三种状态共计五种处理函数。
componentWillMount()
componentDidMount()
componentWillUpdate(object nextProps, object nextState)
componentDidUpdate(object prevProps, object prevState)
componentWillUnmount()
此外,React 还提供两种特殊状态的处理函数。
componentWillReceiveProps(object nextProps):已加载组件收到新的参数时调用
shouldComponentUpdate(object nextProps, object nextState):组件判断是否重新渲染时调用
Initial Render
Props Change
State Change
这里可以看出,Props比State的改变会有多出一个shouldComponentUpdate的回调方法。
Component Unmount
如果需要判断某个组件是否挂载,可以isMounted()方法进行判断,可以用该方法来确保异步调用中的setState与forceUpdate方法不会被误用。不过该方法在ES6的类中已经被移除了,在未来的版本中也会被逐步移除。
总结而言,一个完整的React Component的写法应该如下:
/**
* @jsx React.DOM
*/
var React = require('react'),
MyReactComponent = React.createClass({
// The object returned by this method sets the initial value of this.state
getInitialState: function(){
return {};
},
// The object returned by this method sets the initial value of this.props
// If a complex object is returned, it is shared among all component instances
getDefaultProps: function(){
return {};
},
// Returns the jsx markup for a component
// Inspects this.state and this.props create the markup
// Should never update this.state or this.props
render: function(){
return (<div></div>);
},
// An array of objects each of which can augment the lifecycle methods
mixins: [],
// Functions that can be invoked on the component without creating instances
statics: {
aStaticFunction: function(){}
},
// -- Lifecycle Methods --
// Invoked once before first render
componentWillMount: function(){
// Calling setState here does not cause a re-render
},
// Invoked once after the first render
componentDidMount: function(){
// You now have access to this.getDOMNode()
},
// Invoked whenever there is a prop change
// Called BEFORE render
componentWillReceiveProps: function(nextProps){
// Not called for the initial render
// Previous props can be accessed by this.props
// Calling setState here does not trigger an an additional re-render
},
// Determines if the render method should run in the subsequent step
// Called BEFORE a render
// Not called for the initial render
shouldComponentUpdate: function(nextProps, nextState){
// If you want the render method to execute in the next step
// return true, else return false
return true;
},
// Called IMMEDIATELY BEFORE a render
componentWillUpdate: function(nextProps, nextState){
// You cannot use this.setState() in this method
},
// Called IMMEDIATELY AFTER a render
componentDidUpdate: function(prevProps, prevState){
},
// Called IMMEDIATELY before a component is unmounted
componentWillUnmount: function(){
}
});
module.exports = MyReactComponent;
Props
getDefaultProps
设置默认的Props.
Attributes
children
this.props 对象的属性与组件的属性一一对应,但是有一个例外,就是 this.props.children 属性。它表示组件的所有子节点。
var NotesList = React.createClass({
render: function() {
return (
<ol>
{
this.props.children.map(function (child) {
return <li>{child}</li>
})
}
</ol>
);
}
});
React.render(
<NotesList>
<span>hello</span>
<span>world</span>
</NotesList>,
document.body
);
其效果图如下所示:
enter description here
Validation
React.createClass({
propTypes: {
// You can declare that a prop is a specific JS primitive. By default, these
// are all optional.
optionalArray: React.PropTypes.array,
optionalBool: React.PropTypes.bool,
optionalFunc: React.PropTypes.func,
optionalNumber: React.PropTypes.number,
optionalObject: React.PropTypes.object,
optionalString: React.PropTypes.string,
// Anything that can be rendered: numbers, strings, elements or an array
// containing these types.
optionalNode: React.PropTypes.node,
// A React element.
optionalElement: React.PropTypes.element,
// You can also declare that a prop is an instance of a class. This uses
// JS's instanceof operator.
optionalMessage: React.PropTypes.instanceOf(Message),
// You can ensure that your prop is limited to specific values by treating
// it as an enum.
optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
// An object that could be one of many types
optionalUnion: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.number,
React.PropTypes.instanceOf(Message)
]),
// An array of a certain type
optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
// An object with property values of a certain type
optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
// An object taking on a particular shape
optionalObjectWithShape: React.PropTypes.shape({
color: React.PropTypes.string,
fontSize: React.PropTypes.number
}),
// You can chain any of the above with `isRequired` to make sure a warning
// is shown if the prop isn't provided.
requiredFunc: React.PropTypes.func.isRequired,
// A value of any data type
requiredAny: React.PropTypes.any.isRequired,
// You can also specify a custom validator. It should return an Error
// object if the validation fails. Don't `console.warn` or throw, as this
// won't work inside `oneOfType`.
customProp: function(props, propName, componentName) {
if (!/matchme/.test(props[propName])) {
return new Error('Validation failed!');
}
}
},
/* ... */
});
State
React不提倡数据的双向绑定,而在用户行为下面产生的数据更新,React建议还是通过事件机制来处理。譬如下述例子中,输入框文本内容的改变,还是通过onChange事件,然后出发状态机的变化。
var LikeButton = React.createClass({
getInitialState: function() {
return {liked: false};
},
handleClick: function(event) {
this.setState({liked: !this.state.liked});
},
render: function() {
var text = this.state.liked ? 'like' : 'haven\'t liked';
return (
<p onClick={this.handleClick}>
You {text} this. Click to toggle.
</p>
);
}
});
React.render(
<LikeButton />,
document.getElementById('example')
);
Props与State对比
参考资料
Props VS State
组件的主要职责是将原始数据转化为HTML中的富文本格式,而Props与State协作完成这件事,换言之,Props与State的并集即是全部的原始数据。Props与State之间也是有很多交集的,譬如:
Props与State都是JS对象。
Props与State的值的改变都会触发界面的重新渲染。
Props与State都是确定性的,即在确定的Props或者State的值的情况下都会得出相同的界面。
不过Props顾名思义,更多的是作为Component的配置项存在。Props往往是由父元素指定并且传递给自己的子元素,不过自身往往不会去改变Props的值。另一方面,State在组件被挂载时才会被赋予一个默认值,而常常在与用户的交互中发生更改。往往一个组件独立地维护它的整个状态机,可以认为State是一个私有属性。他们的对比如下:
描述 | Props | State |
---|---|---|
是否可以从父元素获取初始值 | Yes | Yes |
是否可以被父元素改变 | Yes | No |
是否可以设置默认值 | Yes | Yes |
是否可以在组件内改变 | No | Yes |
是否可以设置为子元素的初始值 | Yes | Yes |
是否可以在子元素中改变 | Yes | No |
Multiple Components
Children:组件Tag内包裹
上文中提及过,一种利用组件内包裹的方式动态定义组件的方式,可以利用Props的children属性来获取所有包裹住的Dom对象。
Nested Components
React主打的是组件驱动型编程,往往可以将一个大的组件拆分为几个小的组件,这里以头像控件为例:
var Avatar = React.createClass({
render: function() {
return (
<div>
<ProfilePic username={this.props.username} />
<ProfileLink username={this.props.username} />
</div>
);
}
});
var ProfilePic = React.createClass({
render: function() {
return (
<img src={'https://graph.facebook.com/' + this.props.username + '/picture'} />
);
}
});
var ProfileLink = React.createClass({
render: function() {
return (
<a href={'https://www.facebook.com/' + this.props.username}>
{this.props.username}
</a>
);
}
});
React.render(
<Avatar username="pwh" />,
document.getElementById('example')
);
有时候在某个组件内调用另一个组件,并不会进行渲染,譬如:
class Home extends React.Component {
render() {
return (
<div>
<map/>
</div>
);
}
}
var map = React.createClass({
render: function() {
return (
<div id="map-canvas">
<span>hello</span>
</div>
);
}
});
这里的map并不会被识别,应该把map变为Map,可以参考这里。
Mixins:组件的继承
虽然组件的原则就是模块化,彼此之间相互独立,但是有时候不同的组件之间可能会共用一些功能,共享一部分代码。所以 React 提供了 mixins
这种方式来处理这种问题。Mixin 就是用来定义一些方法,使用这个 mixin 的组件能够自由的使用这些方法(就像在组件中定义的一样),所以 mixin 相当于组件的一个扩展,在 mixin 中也能定义“生命周期”方法。
比如一个定时器的 mixin:
var SetIntervalMixin = {
componentWillMount: function() {
this.intervals = [];
},
setInterval: function() {
this.intervals.push(setInterval.apply(null, arguments));
},
componentWillUnmount: function() {
this.intervals.map(clearInterval);
}
};
var TickTock = React.createClass({
mixins: [SetIntervalMixin], // Use the mixin
getInitialState: function() {
return {seconds: 0};
},
componentDidMount: function() {
this.setInterval(this.tick, 1000); // Call a method on the mixin
},
tick: function() {
this.setState({seconds: this.state.seconds + 1});
},
render: function() {
return (
<p>
React has been running for {this.state.seconds} seconds.
</p>
);
}
});
React.render(
<TickTock />,
document.getElementById('example')
);
React 的 mixins
的强大之处在于,如果一个组件使用了多个 mixins,其中几个 mixins
定义了相同的“生命周期方法”,这些方法会在组件相应的方法执行完之后按 mixins 指定的数组顺序执行。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。