目录:
- 1 组件和组件属性(Component and Props)
- 1.1 组件定义方式
- 1.2 组件嵌套使用
- 1.3 提取组件
- 2 状态和声明周期(State and LifeCycle)
- 2.1 状态值
- 2.2 生命周期
- 3 事件处理
- 4 条件渲染(Conditional Rendering)
- 5 列表和键(Lists and Keys)
- 6 Form 表单
- 7 状态值提升(Lifting State Up)
- 8 组合与继承(Composition and Inheritance)
- 9 结束示例
- 10 总结
1 组件和组件属性(Component and Props)
1.1 组件定义方式
React
基于组件开发,而组件可以使UI进行分离,并且高可复用性,从概念上来说,更像是 js
的函数,往组件传入参数(也就是 this.props
)然后返回整个组件对象,通过 ReactDOM.render
渲染到网页中。
组件定义方式应该有三种:
函数式
定义
// test.html <div id="root"></div> <script type="text/babel"> function Welcome( props ) { return (<h1>Hello React.js, {props.name}<br /></h1>); } </script>
渲染
ReactDOM.render( <Welcome name="Welcome" />, document.getELementById('root') );
最终会输出结果:‘Hello React.js, Welcome’
上面函数的参数
props
也就是组件render
时候内部的一些属性值集合这种方式会发现并不需要组件函数中存在
render
方法,这个应该只在 类继承和React.createClass
中才需要。下面看看类继承方式类继承
类继承需要用到 ES6 语法中的
class
,通过React.Component
来创建被继承的组件对象,如下:类继承方式定义组件:(这里注意,
React.Component
是个组件类,不是构造函数,直接使用{}
即可)class Welcome extends React.Component { render() { return (<h1>Hello React.js, { this.props.name}<br /></h1>); } }
渲染方式都是一样的,通过组件名
Welcome
去渲染即可ReactDOM.render( <Welcome name="Welcome" />, document.getELementById('root') );
最终结果和函数式组件定义一样
React.createClass
这里使用的是
React
自己的创建组件类的方法去实现,如果对 es6 不太熟悉的话,这种方式更容易接受点定义:
var Welcome = React.createClass({ render: function () { return (<h1>Hello React.js, { this.props.name}<br /></h1>); } });
渲染:
ReactDOM.render( <Welcome name="Welcome" />, document.getELementById('root') );
其实不管使用上面哪种方式,最后生成的组件在 DOM
树中呈现的结果都是一致的,如下:
<h1 data-reactid=".1">
<span data-reactid=".1.0">Hello React.js, </span>
<span data-reactid=".1.1">Welcome</span>
<br data-reactid=".1.2">
</h1>
还有需要注意的是,不论上面那种方式,Welcome
组件名的首字母一定要大写,否则会报错,并且报错内容都是一样的:‘Uncaught ReferenceError: Welcome is not define’
我们看到上面的 ReactDOM.render
方法里面是直接传的组件形式,事实上 react
也只是变量,比如:
上面的 <Welcome name="Welcome" />
可以定义成变量,再传入到渲染函数
const element = <Welcome name="Welcome" />;
ReactDOM.render( element, document.getElementById( 'root' ) );
结果显示上面方式也是 OK 的,那后面的 id
肯定也是可以通过变量传入的,最后就变成如下:
const element = <Welcome name="Welcome" />;
const id = document.getElementById( 'root' );
ReactDOM.render( element, id );
这样是不是就清爽许多了……
从官文上有一段注意点:
Caveat:
Always start component names with a capital letter.
For example,represents a DOM tag, but represents a component and requires Welcome to be in scope.
这里也说明了为什么组件首字母要大小写原因之一:意思就是说 react
会把消息开头的当作 DOM
标签,而把大写开头的才会当成是个组件名称。
1.2 组件嵌套使用
由于组件一旦定义完成,它就是个以独立的个体形式存在,也就是说它可以被单独渲染到 DOM 树中,同时也可以被其它组件引用,作为其输出,并且支持多次使用
如示例:(不仅被其他组件引用,还能多次重复使用)
// test.html
// Welcome 组件上面已经定义好,直接拿来使用即可
var Wrap = React.createClass({
return (
<div>
<Welcome name="Lizc" />
<Welcome name="Fll" />
<Welcome name="Liwy" />
</div>
);
});
const element = <Wrap />;
const id = document.getElementById( 'root' );
ReactDOM.render( element, id );
输出结果:
Hello React.js, Lizc
Hello React.js, Fll
Hello React.js, Liwy
1.3 提取组件
官文上有这么一句话:‘Don’t be afraid to split components into smaller components.’
意思就是告诉我们不要害怕将组件分割成更小的组件,也就是说我们可以根据我们的需求尽可能的将内容进行组件化,我们来看下官网的示例程序
// extract-component.html
// 样式
<style>
.avatar {
width : 100px;
height : 100px;
}
</style>
// 脚本
<script type="text/babel">
const rootEle = document.getElementById( 'root' );
var Comment = React.createClass({
render: function () {
return (
<div className="comment">
<div className="user-info">
<img className="avatar"
src={this.props.author.avatarUrl}
alt={this.props.author.name}
/>
</div>
<div className="user-info-name">
作者:{this.props.author.name}
</div>
<div className="comment-text">
问候语:{this.props.text}
</div>
<div className="comment-date">
日期:{this.props.date}
</div>
</div>
);
}
});
var element = <Comment
author={
{ avatarUrl:'./images/ygr-01.jpg', name: 'lizc' }}
text='hello react.js'
date={
new Date().toString()}
/>
ReactDOM.render( element, rootEle );
</script>
从上面的 Comment
组件看,其实里面包含四个部分(头像,作者,问候语,日期),本着官文给我们的注解,尽可能的组件化,那么这四个部分也就应该成为独立的组件,下面来实现下这四个组件,而数据这块,依然采用父组件中的属性
这里只是个示范,更多的组件化可以根据自己的需求来进行分割
头像组件
// extract-component.html // 父组件 var Comment = React.createClass({ render: function () { return ( // 父组件 Comment 中引用子组件:头像组件(AvatarComp) <div className="comment"> <AvatarComp author={ this.props.author} /> // ... 其他 </div> ); } }); // 子组件 var AvatarComp = React.createClass({ render: function () { return ( <div className="user-info"> <img className="avatar" src={ this.props.author.avatarUrl} alt={ this.props.author.name} /> </div> ); } });
最终运行结果和之前一致,说明头像组件化成功,另外可以发现,我们的
AvatarComp
组件是定义在父组件Comment
之后的,但是依然可以运行。作者组件
作者组件:
// 父组件中引用作者组件 <AuthorComp name={ this.props.author.name} /> var AuthorComp = React.createClass({ render: function () { return ( <div className="user-info-name"> 作者:{ this.props.name} </div> ); } });
问候语组件
var WelcomeComp = React.createClass({ render: function () { return ( <div className="comment-text"> 问候语:{ this.props.text} </div> ); } }); // 引用 <WelcomeComp text={ this.props.text} />
日期组件
var DateComp = React.createClass({ render: function () { return ( <div className="comment-date"> 日期:{ this.props.date} </div> ); } }); // 引用 <DateComp date={ new Date().toString()} />
最终完整代码:虽然在代码量上貌似多了点,可是这样依赖每个功能都成为了单独的一个组件,可以在任何地方使用它,而父组件中直接引用子组件,也让父组件更容易维护,修改,并且更加直观,总之好处多多。
<script type="text/babel">
const rootEle = document.getElementById( 'root' );
var Comment = React.createClass({
render: function () {
return (
<div className="comment">
<AvatarComp author={
this.props.author} />
<AuthorComp name={
this.props.author.name} />
<WelcomeComp text={
this.props.text} />
<DateComp date={
new Date().toString()} />
</div>
);
}
});
var AvatarComp = React.createClass({
render: function () {
return (
<div className="user-info">
<img className="avatar"
src={
this.props.author.avatarUrl}
alt={
this.props.author.name}
/>
</div>
);
}
});
var AuthorComp = React.createClass({
render: function () {
return (
<div className="user-info-name">
作者:{
this.props.name}
</div>
);
}
});
var WelcomeComp = React.createClass({
render: function () {
return (
<div className="comment-text">
问候语:{
this.props.text}
</div>
);
}
});
var DateComp = React.createClass({
render: function () {
return (
<div className="comment-date">
日期:{
this.props.date}
</div>
);
}
});
var element = <Comment
author={
{ avatarUrl:'./images/ygr-01.jpg', name: 'lizc' }}
text='hello react.js'
date={
new Date().toString()}
/>
ReactDOM.render( element, rootEle );
</script>
2 状态和声明周期(State and LifeCycle)
2.1 状态值
React
的数据传递都是单向传递的,而对于需要变化的值,则是通过 state
对象来控制,比如下面的时钟例子,就是通过每秒更新 state
状态的值去刷新显示结果
这有个时钟计时的组件示例:
// lifecycle-state.html