React学习笔记
来自 《React 引领未来的用户界面开发框架》一书
JSX
JavaScript XML,一种在React组建内部构建标签的类XML语法,实际会被转换成JavaScript函数。JSX的表达能力很强,也很适合组件化的React开发。
范例
//编写一个Divider
var Divider = React.createClass({
render: function(){
<div className="divider">
<h2>{this.props.children}</h2><hr/>
</div>
}
})
//Divider调用
<Divider>Questions</Divider>
非DOM属性
key
可选的唯一标志符,便于React渲染时重用ref
表示父子组建之间的引用dangerouslySetInnerHTML
在需要将HTML内容设置为字符串时使用
事件
事件已经被规范化,并同统一用驼峰形式表示。
handleClick: function(event){...},
render: function(){
return <div onClick={this.handleClick}>...</div>
}
实际上,如CSS的表示也是用这样的形式。
组建生命周期
首次使用组件时
getDefaultProps
可以为实例设置默认的props
值getInitialState
初始化每个实例初始化state
值componentWillMount
在render
之间最后修改state
值的机会render
创建虚拟DOM,是组件中唯一必要的方法componentDidMount
可以在该方法中通过调用this.getDOMNode()
来访问真实的DOM,可以在这里调用如JQuery等工具。如果React运行在服务端,该方法不会被调用
之后使用该组件时
getInitialState
componentWillMount
render
componentDidMount
应用状态改变时
componentWillReceivProps
组件的props可以通过父辈组件来修改,此时该方法将被调用shouldComponentUpdate
在渲染前判断是否需要更新,做到精确的优化,不要草率的使用该方法componentWillUpdate
不可以在该方法中更新state
或者props
值render
componentDidUpdate
和componentDidMount
类似
销毁时
调用componentWillUnmount
,可以在这个方法中做一些清理工作,比如创建的定时器或者添加的事件监听器
数据流
React中数据流是单向的,从父节点传递到子节点,因而组件是简单且易于把握的,组件只需要从父节点获取props渲染即可。如果顶层组件的某个prop改变了,React会递归地向下遍历整棵组件树,重新渲染所有使用这个属性的组件。
Props
Props即为properties,能够把任意类型的数据传递给组件。
//挂载数据时设置组件的props
var components=[{title:'test_0'},{title:'test_1'}];
<ListComponents components={components}/>
可以通过this.props
来访问组件的props
,但不能通过这种方式修改它,一个组件绝对不可以自己修改自己的props
。props
可以是字符串,对象,事件处理器等。可以使用PropTypes
来验证props
的类型,推荐使用。
State
每一个React组件都可以拥有自己的state,其与props的区别是,state只存在于组件内部
state
的值应该通过this.setState()
修改,只要该状态被修改,render
方法就会被调用,然后导致之后的一系列变化。状态总是让组件变得更加复杂,把状态针对不同的组件独立开来,能更有利于调试。
Props和State存放数据
state
中不要保存计算出的值,而应该保存最简单的数据,即为组件工作时的必要数据。而props
用来作为数据源,保证数据的单向流动。
事件处理
绑定方式实际上和JavaScript并没太大差别,例如:
var Demo = React.createClass({
render: function(){
<div onClick={this.handleClick}>Click here</div>
}
handleClick: function(event){
this.setState({isClicked : true});
}
});
setState
合并对象的方式来更新组件的状态
replaceState
将state
整个替换为新的
组件的复合
React的一个突出特点是构建组件的能力,实际上React代码主要就是构建组件,就像编写HTML文档时使用元素一样。
// 封装一个radio input作为子组件
var AnswerRadioInput = React.createClass({
propTypes : {
id: React.PropTypes.string,
name: React.PropTypes.string.isRequired,
label: React.PropTypes.string.isRequired,
value: React.PropTypes.string.isRequired,
checked: React.PropTypes.bool
},
getDefaultProps : function(){
retrun{
id: null,
checked:false
}
},
getInitialState : function(){
var id = this.props.id? this.props.id : uniqueId('radio-');
return {
checked : this.props.checked,
id : id,
name : id
}
},
render : function(){
return (
<div className="radio">
<!-- htmlFor表示该Label为哪个控件服务,点击该label时,所比绑定的元素将获得焦点 -->
<label htmlFor={this.props.id}>
<input type="radio"
name={this.props.name}
id={this.props.id}
value={this.props.value}
checked={this.state.checked}/>
{this.props.label}
</label>
</div>
)
}
});
以上构建了一个AnswerRadioInput
组件,之后构建一个AnswerMultipleChoiceQuestion
来组合这一组件。
// 父组件
var AnswerMultipleChoiceQuestion = React.createClass({
propTyes: {
value: React.PropTypes.string,
choices: React.PropTypes.array.isRequired,
onCompleted: React.PropTyes.func.isRequired
},
getInitialState: function(){
return {
id: uniqueId('multiple-choice-'),
value: this.props.value
}
},
renderChoices: function(){
return this.props.choices.map(function(choice,i){
return AnswerRadioInput({
id: "choice-"+i,
name: this.state.id,
label: choice,
value: choice,
checked: this.state.value === choice
});
}.bind(this))
},
render: function(){
return (
<div className="form-group">
<label className="survey-item-label" htmlFor={this.state.id}>
{this.props.label}
</label>
<div className="survey-item-content">
{this.renderChoices()}
</div>
</div>
);
}
});
此时存在一个问题,子组件没办法把自身的变化通知给父组件,因此,父组件需要关联子组件才能知道其更新。方法实际上就是回调,父组件通过向子组件中传入一个回调函数,子组件在需要时调用。
//父组件传入回调函数
var AnswerMultipleChoiceQuestion = React.createClass({
...
handleChanged: function(value){
this.setState({value:value});
this.props.onCompleted(value);
},
renderChoices: function(){
return this.props.choices.map(function(choice, i){
return AnswerRadioInput({
...
onChanged: this.handleChanged
});
}.bind(this));
},
});
//子组件调用回调
var AnswerRadioInpu = React.createClass({
propTypes: {
...
onChanged: React.PropTypes.func.isRequired
},
handleChanged: function(event){
//event.target 找到事件的触发对象input
var checked = event.target.checked;
this.setState({checked: checked});
if(checked){
this.props.onChange(this.props.value);
}
},
render: function(){
return(
<div className="radio">
<label htmlFor={this.state.id}>
<input type="radio"
...
onChange={this.handleChanged}/>
{this.props.label}
</label>
</div>
)
}
})
Mixin
mixin允许我们定义可以在多个组件中共用的方法
// 定义IntervalMixin
var IntervalMixin = {
setInterval: function(callback, interval){
var token = setInterval(callback, interval);
this.__intervals.push(token);
return token;
},
componentDidMount: function(){
this.__intervals=[];
},
componentWillUnmount: function(){
this.__intervals.map(clearInterval);
}
};
//定义组件
var Since2014 = React.createClass({
//实际相当于把mixins中的内容解构了出来,放在了Since2014中
mixins: [IntervalMixin],
componentDidMount: function(){
this.setInterval(this.forceUpdate.bind(this),1000);
},
render: function() {
var from = Number(new Date(2014, 0, 1));
var to =Date.now();
return (
<div>{Math.round((to-from)/1000)}</div>
);
}
});
DOM操作
在某些情况下,为了实现某些需求就不得不去操作底层的DOM。最常见的场景包括:需要与一个没有使用React的第三方类库进行整合,或者执行一个React没有原生支持的操作。
var DoodleArea = React.createClass({
render: function(){
//通过设定一个ref属性,之后可以通过this.refs.mainCanvas来访问到<canvas>组件
return <canvas ref="mainCanvas"/>;
},
componentDisMount: function(){
//在这里通过getDOMNode()方法来访问到底层的DOM节点,也可以在事件处理器中调用
var canvasNode= this.refs.mainCanvas.getDOMNode();
}
});
表单
无约束组件
组件本身控制其值
var MyForm = React.createClass({
submitHandler: function(event){
event.preventDefault();
var text=this.refs.text.getDOMNode().value;
alert(helloTo);
},
render: function(){
return (
<form onSubmit={this.submitHandler}>
<input ref="text" type="text" defaultValue="Hello world!"/>
<br/>
<button type="submit">Submit</button>
</form>
);
}
});
约束组件
约束组件的值被存储在state中,由React控制
var MyForm = React.createClass({
getInitialState: function(){
return {
text: 'hello world!'
}
},
…
render: function(){
return (
<form onSubmit={this.submitHandler}>
<input type="text" value={this.state.text}
onChange={this.handleChange}/>
...
</form>
);
}
});
对于如handleChange
方法,还可以用bind
方法传递更多参数,例如this.handleChange.bind(this, "name")
。
React Router
React的介绍即讲过其非常适合于单页面应用,页面之间的切换需要Router
的帮助。
React-router完全由React Component构成,路由被定义为了组件,路由的处理器也是组件
//react router的写法如下
var appRouter=(
<Routes location="history">
<Route title="SurveryBuilder" handler={App}>
<Route name="list" path="/" handler={ListSurveys}/>
<Route title="Add survey to surveyBuilder" name="add"
path="/add_survey" handler={AddSurvey}/>
<NotFound title="Page Not Found" handler={NotFoundHandler}/>
</Route>
</Routes>
);
每一个处理器就对应着特定页面的组件,需要将上面的路由作为最顶层的组件渲染来启动。
使用react-router Link
组件来进行导航。