React入门
包含
是什么
为什么
怎么用
搭建示例应用
不包含
生态圈/公用组件
应用框架
编译/合并代码
是什么
react 是一个做 UI 的库,具体来说是做 UI 组件的库,专注于做 mvc 中的 v.
简单的 REACT 组件
<div id="container">
</div>
<script>
React.createClass({
render: function() {
return <div>Hello {this.props.name}</div>;
}
});
React.render(<Hello name="World" />, document.getElementById('container'));
</script>
用状态控制组件变化
可以把一个组件看做一个状态机, 每一次状态对应于组件的一个 ui
var Timer = React.createClass({
getInitialState: function() {
return {secondsElapsed: 0};
},
tick: function() {
this.setState({secondsElapsed: this.state.secondsElapsed + 1});
},
componentDidMount: function() {//插入真实DOM之后被执行
this.interval = setInterval(this.tick, 1000);
},
componentWillUnmount: function() { //移除DOM之前被执行
clearInterval(this.interval);
},
render: function() {
return (
<div>Seconds Elapsed: {this.state.secondsElapsed}</div>
);
}
});
React.render(<Timer />, document.getElementById('container'));
为什么
api 少,类库易学
组件内聚,易于组合
原生组件和自定义组件融合渲染
状态/属性驱动全局更新,不用关注细节更新
commonjs 生态圈/工具链完善
怎么用
jsx
event
composition
props/state
mixin
form
refs
component api
top api
JSX
类似 xml 的语法,用来描述组件树
<div classname="x">
<a href="#">#</a>
<component x="y">1</component>
</div>
React.createElement('div',{
className:'x'
},[
React.createElement('a',{href:'#'},'#'),
React.createElement(Component,{x:'y'},1);
]);
注意和 html 语法不太一样,比如必须是驼峰命名, 以及属性名不能和 js 关键字冲突,例如:className,readOnly
JSX 嵌入变量
可以通过 {变量名} 来将变量的值作为属性值
var x = 'http://www.baidu.com';
var y = <a href= {x} target="_blank">baidu.com</a>;
React.render(y, document.getElementById('container'));
JSX SPREAD
可以用通过 {…obj} 来批量设置一个对象的键值对到组件的属性,注意顺序
var x = 'http://www.baidu.com';
var obj = {
href:"http://www.taobao.com",
target:"_blank"
}
var y = <a {...obj} href= {x}>baidu.com</a>;
React.render(y, document.getElementById('container'));
PROPS
通过 this.props 可以获取传递给该组件的属性值,还可以通过定义 getDefaultProps 来指定默认属性值
var B = React.createClass({
getDefaultProps(){
return {
title:'default'
};
},
render(){
return <b>{this.props.title}</b>
}
});
React.render(<div>
<B title="指定 "/> <B/>
</div>,document.getElementById('container'));
PROP VALIDATION
通过指定 propTypes 可以校验属性值的类型
var B = React.createClass({
propTypes: {
title: React.PropTypes.string,
},
getDefaultProps(){
return {
title:'default'
};
},
render(){
return <b>{this.props.title}</b>
}
});
React.render(<div>
<B title="指定 "/> <B title={2}/>
</div>,document.getElementById('container'));
注意:校验仅为提升开发者体验
STATE
组件内部的状态,可以使用 state
var Timer = React.createClass({
getInitialState: function() {
return {secondsElapsed: 0};
},
tick: function() {
this.setState({secondsElapsed: this.state.secondsElapsed + 1});
},
componentDidMount: function() {
this.interval = setInterval(this.tick, 1000);
},
componentWillUnmount: function() {
clearInterval(this.interval);
},
render: function() {
return (
<div>Seconds Elapsed: {this.state.secondsElapsed}</div>
);
}
});
React.render(<Timer />, document.getElementById('container'));
EVENT
可以通过设置原生 dom 组件的 onEventType 属性来监听 dom 事件,例如 onClick, onMouseDown,在加强组件内聚性的同时,避免了传统 html 的全局变量污染
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('container'));
注意:事件回调函数参数为标准化的事件对象,可以不用考虑 ie
组合
可以像使用原生 dom 组件一样使用自定义的组件
var A = React.createClass({
render(){
return <a href='#'>a</a>
}
});
var B = React.createClass({
render(){
return <i><A /> !</i>;
}
});
React.render(<B />,document.getElementById('container'));
组合 child
自定义组件中可以通过 this.props.children 访问自定义组件的子节点
var B = React.createClass({
render(){
return <ul>
{React.Children.map(this.props.children,function(c){
return <li>{c}</li>;
})}
</ul>;
}
});
React.render(<B>
<a href="#">1</a>
2
</B>,document.getElementById('container'));
MIXIN
mixin 是一个普通对象,通过 mixin 可以在不同组件间共享代码
var mixin = {
propTypes: {
title: React.PropTypes.string,
},
getDefaultProps(){
return {
title:'default'
};
},
};
var A = React.createClass({
mixins: [mixin],
render(){
return <i>{this.props.title}</i>
}
});
var B = React.createClass({
mixins: [mixin],
render(){
return <b>{this.props.title}</b>
}
});
React.render(<div>
<B/> <A title={2}/>
<A/>
</div>,document.getElementById('container'));
FORM
和 html 的不同点:
value/checked 属性设置后,用户输入无效
textarea 的值要设置在 value 属性
select 的 value 属性可以是数组,不建议使用 option 的 selected 属性
input/textarea 的 onChange 用户每次输入都会触发(即使不失去焦点)
radio/checkbox 点击后触发 onChange
控制组件
如果设置了 value 属性,那么改组件变为受控组件,用户无法输入,除非程序改变 value 属性
var Test = React.createClass({
render(){
return <input value="x" />
}
});
React.render(<Test />,document.getElementById('container'));
可以通过监听 onChange 事件结合 state 来改变 input 的值
var Test = React.createClass({
getInitialState(){
return {
value:'xasdasdf'
};
},
onChange(e){
this.setState({
value:e.target.value
});
},
render(){
return <input value={this.state.value} onChange={this.onChange}/>
}
});
React.render(<Test />,document.getElementById('container'));
不受控制的组件
设置 defaultValue 为设置 input 的初始值,之后 input 的值由用户输入
var Test = React.createClass({
render(){
return <input defaultValue="xyz" />
}
});
React.render(<Test />,document.getElementById('container'));
REF
该功能是为了结合现有非 react 类库,通过 ref/refs 可以取得组件实例,进而取得原生节点
var Test = React.createClass({
componentDidMount(){
alert(React.findDOMNode(this.refs.content).innerHTML);
},
render(){
return <div>
<h3>header</h3>
<div ref='content'>content</div>
</div>;
}
});
React.render(<Test />,document.getElementById('container'));
组件的API
React.createClass 定义组件时允许传入的配置
getDefaultProps 得到默认属性对象
propTypes 属性检验规则
mixins 组件间公用方法
组件生命周期
React.createClass 定义时允许传入的函数,会在特定生命周期内调用
初次创建组件时调用
getInitialState 得到初始状态对象
render 返回组件树. 必须设置
componentDidMount 渲染到 dom 树中是调用,只在客户端调用,可用于获取原生节点
组件的属性值改变时调用
componentWillReceiveProps 属性改变调用
shouldComponentUpdate 判断是否需要重新渲染
render 返回组件树. 必须设置
componentDidUpdate 渲染到 dom 树中是调用, 可用于获取原生节点
最后是 componentWillUnmount 组件从 dom 销毁前调用
组件生命周期的演示
function log(str){
document.getElementById('log').innerHTML+='<p>'+str+'</p>';;
}
document.getElementById('clear').onclick=function(){
document.getElementById('log').innerHTML='';
};
var Test = React.createClass({
getInitialState() {
log('getInitialState');
return {
value: this.props.value
};
},
componentWillReceiveProps(nextProps){
log('componentWillReceiveProps');
this.setState({
value: nextProps.value
});
},
shouldComponentUpdate(nextProps,nextState){
log('shouldComponentUpdate');
return true;
},
componentWillUpdate(nextProps,nextState){
log('componentWillUpdate');
},
componentWillMount(){
log('componentWillMount');
},
render() {
log('render');
return <span>{this.props.value}</span>
},
componentDidMount() {
log('componentDidMount');
},
componentDidUpdate(prevProps,prevState) {
log('componentDidUpdate');
},
componentWillUnmount(prevProps,prevState) {
log('componentWillUnmount');
}
});
var Hello = React.createClass({
getInitialState() {
return {
value:1,
destroyed:false
};
},
increase() {
this.setState({
value: this.state.value+1
});
},
destroy() {
this.setState({
destroyed: true
});
},
render: function() {
if(this.state.destroyed){
return null;
}
return <div>
<p>
<button onClick={this.increase}>increase</button>
<button onClick={this.destroy}>destroy</button>
</p>
<Test value={this.state.value}/>
</div>;
}
});
React.render(<Hello />, document.getElementById('container'));
TOP API
React.createClass 创建组件类
React.findDOMNode 从组件实例获取 dom 根节点
React.render 渲染组件到 dom
React.Children.* 操作 map/forEach children 工具类
一步步搭建一个评论应用
var Comment = React.createClass({
render: function () {
var rawMarkup = marked(this.props.children.toString(), {sanitize: true});
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
<span dangerouslySetInnerHTML={{__html: rawMarkup}} />
</div>
);
}
});
var CommentList = React.createClass({
render: function () {
var commentNodes = this.props.data.map(function (comment) {
return (
<Comment author={comment.author}>
{comment.text}
</Comment>
);
});
return (
<div className="commentList">
{commentNodes}
</div>
);
}
});
var CommentForm = React.createClass({
getInitialState() {
return {
name: '',
text: ''
}
},
updateField(field, e) {
var state = {};
state[field] = e.target.value;
this.setState(state);
},
handleSubmit(e){
e.preventDefault();
this.props.onPost({
author:this.state.name,
text:this.state.text
});
this.setState({
name:'',
text:''
});
},
render: function () {
return (
<form className="commentForm" onSubmit={this.handleSubmit}>
<input placeholder="Your name" value={this.state.name} onChange={this.updateField.bind(this, 'name')}/>
<input placeholder="Say something..."
value={this.state.text} onChange={this.updateField.bind(this, 'text')}
/>
<input type="submit" value="Post" />
</form>
);
}
});
var database=[
{
author: '作者 1',
text: '评论 1,' + Date.now()
},
{
author: '作者 2',
text: ' *评论 2,' + Date.now() + '* '
}
];
var CommentBox = React.createClass({
loadCommentsFromServer: function () {
var self = this;
$.ajax({
url: this.props.url,
method:'post',
dataType:'json',
data: {
json:JSON.stringify({
data:database
})
},
success(res) {
self.setState({data: res.data})
}
});
},
getInitialState: function () {
return {data: []};
},
handlePost(post){
database.push(post);
this.loadCommentsFromServer();
},
componentDidMount: function () {
this.loadCommentsFromServer();
},
render: function () {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList data={this.state.data} />
<CommentForm onPost={this.handlePost}/>
</div>
);
}
});
React.render(
<CommentBox url="/echo/json/" />,
document.getElementById('container')
);
组件分解
顶层 CommentBox
评论列表 CommentList
单条评论 Comment
评论表单 CommentForm
最简单的 COMMENTBOX
var CommentBox = React.createClass({
render: function() {
return (
<div className="commentBox">
Hello, world! I am a CommentBox.
</div>
);
}
});
React.render(
<CommentBox />,
document.getElementById('container')
);
组合组件
var CommentList = React.createClass({
render: function() {
return (
<div className="commentList">
Hello, world! I am a CommentList.
</div>
);
}
});
var CommentForm = React.createClass({
render: function() {
return (
<div className="commentForm">
Hello, world! I am a CommentForm.
</div>
);
}
});
var CommentBox = React.createClass({
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList />
<CommentForm />
</div>
);
}
});
React.render(
<CommentBox />,
document.getElementById('container')
);
属性传递
var Comment = React.createClass({
render: function() {
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
{this.props.children}
</div>
);
}
});
var CommentList = React.createClass({
render: function() {
return (
<div className="commentList">
<Comment author="作者 1">评论 1</Comment>
<Comment author="作者 2">评论 2</Comment>
</div>
);
}
});
var CommentForm = React.createClass({
render: function() {
return (
<div className="commentForm">
Hello, world! I am a CommentForm.
</div>
);
}
});
var CommentBox = React.createClass({
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList />
<CommentForm />
</div>
);
}
});
React.render(
<CommentBox />,
document.getElementById('container')
);
使用 DOM 库 MARKED
var Comment = React.createClass({
render: function() {
var rawMarkup = marked(this.props.children.toString(), {sanitize: true});
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
<span dangerouslySetInnerHTML={{__html: rawMarkup}} />
</div>
);
}
});
var CommentList = React.createClass({
render: function() {
return (
<div className="commentList">
<Comment author="作者 1">评论 1</Comment>
<Comment author="作者 2"> *评论 2* </Comment>
</div>
);
}
});
var CommentForm = React.createClass({
render: function() {
return (
<div className="commentForm">
Hello, world! I am a CommentForm.
</div>
);
}
});
var CommentBox = React.createClass({
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList />
<CommentForm />
</div>
);
}
});
React.render(
<CommentBox />,
document.getElementById('container')
);
数据分离
var data = [
{author: "作者 1", text: "评论 1"},
{author: "作者 2", text: "*评论 2*"}
];
var Comment = React.createClass({
render: function() {
var rawMarkup = marked(this.props.children.toString(), {sanitize: true});
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
<span dangerouslySetInnerHTML={{__html: rawMarkup}} />
</div>
);
}
});
var CommentList = React.createClass({
render: function() {
var commentNodes = this.props.data.map(function (comment) {
return (
<Comment author={comment.author}>
{comment.text}
</Comment>
);
});
return (
<div className="commentList">
{commentNodes}
</div>
);
}
});
var CommentForm = React.createClass({
render: function() {
return (
<div className="commentForm">
Hello, world! I am a CommentForm.
</div>
);
}
});
var CommentBox = React.createClass({
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList data={data}/>
<CommentForm />
</div>
);
}
});
React.render(
<CommentBox />,
document.getElementById('container')
);
从服务器取得数据
var Comment = React.createClass({
render: function () {
var rawMarkup = marked(this.props.children.toString(), {sanitize: true});
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
<span dangerouslySetInnerHTML={{__html: rawMarkup}} />
</div>
);
}
});
var CommentList = React.createClass({
render: function () {
var commentNodes = this.props.data.map(function (comment) {
return (
<Comment author={comment.author}>
{comment.text}
</Comment>
);
});
return (
<div className="commentList">
{commentNodes}
</div>
);
}
});
var CommentForm = React.createClass({
render: function () {
return (
<div className="commentForm">
Hello, world! I am a CommentForm.
</div>
);
}
});
var CommentBox = React.createClass({
loadCommentsFromServer: function () {
var self = this;
new Request.JSON({
url: this.props.url,
data: {
json: JSON.encode({
data: [
{
author: '作者 1',
text: '评论 1,' +Date.now()
},
{
author: '作者 2',
text: ' *评论 2,'+Date.now()+'* '
}
]
}),
delay:1
},
onSuccess(res) {
self.setState({data: res.data})
}
}).send();
},
getInitialState: function () {
return {data: []};
},
componentDidMount: function () {
this.loadCommentsFromServer();
setInterval(this.loadCommentsFromServer, this.props.pollInterval);
},
render: function () {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList data={this.state.data} />
<CommentForm />
</div>
);
}
});
React.render(
<CommentBox url="/echo/json/" pollInterval={2000} />,
document.getElementById('container')
);
评论表单
var Comment = React.createClass({
render: function () {
var rawMarkup = marked(this.props.children.toString(), {sanitize: true});
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
<span dangerouslySetInnerHTML={{__html: rawMarkup}} />
</div>
);
}
});
var CommentList = React.createClass({
render: function () {
var commentNodes = this.props.data.map(function (comment) {
return (
<Comment author={comment.author}>
{comment.text}
</Comment>
);
});
return (
<div className="commentList">
{commentNodes}
</div>
);
}
});
var CommentForm = React.createClass({
getInitialState() {
return {
name: '',
text: ''
}
},
updateField(field, e) {
console.log(e);
var state = {};
state[field] = e.target.value;
this.setState(state);
},
handleSubmit(e){
e.preventDefault();
this.props.onPost({
name:this.state.name,
text:this.state.text
});
this.setState({
name:'',
text:''
});
},
render: function () {
return (
<form className="commentForm" onSubmit={this.handleSubmit}>
<input placeholder="Your name" value={this.state.name} onChange={this.updateField.bind(this, 'name')}/>
<input placeholder="Say something..."
value={this.state.text} onChange={this.updateField.bind(this, 'text')}
/>
<input type="submit" value="Post" />
</form>
);
}
});
var database=[
{
author: '作者 1',
text: '评论 1,' + Date.now()
},
{
author: '作者 2',
text: ' *评论 2,' + Date.now() + '* '
}
];
var CommentBox = React.createClass({
loadCommentsFromServer: function () {
var self = this;
$.ajax({
url: this.props.url,
method:'post',
dataType:'json',
data: {
json:JSON.stringify({
data:database
})
},
success(res) {
console.log(res)
self.setState({data: res.data})
}
});
},
getInitialState: function () {
return {data: []};
},
handlePost(){
},
componentDidMount: function () {
this.loadCommentsFromServer();
},
render: function () {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList data={this.state.data} />
<CommentForm onPost={this.handlePost}/>
</div>
);
}
});
React.render(
<CommentBox url="/echo/json/" />,
document.getElementById('container')
);
最后:通知重新渲染
var Comment = React.createClass({
render: function () {
var rawMarkup = marked(this.props.children.toString(), {sanitize: true});
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
<span dangerouslySetInnerHTML={{__html: rawMarkup}} />
</div>
);
}
});
var CommentList = React.createClass({
render: function () {
var commentNodes = this.props.data.map(function (comment) {
return (
<Comment author={comment.author}>
{comment.text}
</Comment>
);
});
return (
<div className="commentList">
{commentNodes}
</div>
);
}
});
var CommentForm = React.createClass({
getInitialState() {
return {
name: '',
text: ''
}
},
updateField(field, e) {
var state = {};
state[field] = e.target.value;
this.setState(state);
},
handleSubmit(e){
e.preventDefault();
this.props.onPost({
author:this.state.name,
text:this.state.text
});
this.setState({
name:'',
text:''
});
},
render: function () {
return (
<form className="commentForm" onSubmit={this.handleSubmit}>
<input placeholder="Your name" value={this.state.name} onChange={this.updateField.bind(this, 'name')}/>
<input placeholder="Say something..."
value={this.state.text} onChange={this.updateField.bind(this, 'text')}
/>
<input type="submit" value="Post" />
</form>
);
}
});
var database=[
{
author: '作者 1',
text: '评论 1,' + Date.now()
},
{
author: '作者 2',
text: ' *评论 2,' + Date.now() + '* '
}
];
var CommentBox = React.createClass({
loadCommentsFromServer: function () {
var self = this;
$.ajax({
url: this.props.url,
method:'post',
dataType:'json',
data: {
json:JSON.stringify({
data:database
})
},
success(res) {
self.setState({data: res.data})
}
});
},
getInitialState: function () {
return {data: []};
},
handlePost(post){
database.push(post);
this.loadCommentsFromServer();
},
componentDidMount: function () {
this.loadCommentsFromServer();
},
render: function () {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList data={this.state.data} />
<CommentForm onPost={this.handlePost}/>
</div>
);
}
});
React.render(
<CommentBox url="/echo/json/" />,
document.getElementById('container')
);
实现一个 checkbox
功能点
新生成组件/更新组件
checked/unchecked 交互处理
键盘
label?
var Checkbox = React.createClass({
getDefaultProps() {
return {
defaultChecked: false,
onChange(){}
};
},
getInitialState() {
var state = {};
var props = this.props;
if('checked' in props){
state.checked = props.checked;
} else {
state.checked = props.defaultChecked;
}
return state;
},
componentWillReceiveProps(newProps){
// 组件重新渲染了,属性可能有改变,同步属性到状态
if('checked' in newProps){
this.setState({
checked: newProps.checked
});
}
},
onClick() {
var nextChecked = !this.state.checked;
if(!('checked' in this.props)){
// 非受限
this.setState({
checked: nextChecked
});
}
// 回调函数通知外部
this.props.onChange(nextChecked);
},
render() {
var state = this.state;
var style = {border:'1px solid red',display:'inline-block',width:100,height:100}
if(state.checked){
style.backgroundColor='red';
}
return <span style={style} onClick={this.onClick}></span>;
}
});
var Test = React.createClass({
getInitialState(){
return {
checked: false
};
},
onChange(){
this.setState({
checked: !this.state.checked
});
},
render(){
return <div>
<p>受限: <Checkbox checked={this.state.checked} /></p>
<p>不受限: <Checkbox defaultChecked={true} onChange={this.onChange}/></p>
</div>;
}
});
React.render(<Test />, document.getElementById('container'));
说明:from yiminghe