环境配置:
首先根据官网的教程,建立一个简单的工程需要引入三个文件:react.js、react-dom.js、babel.min.js。其中bable.min.js可以是教程中的链接,也可以是自己下载的文件直接拷贝进去,然后直接使用即可,这里需要申明js的类型为text/babel。
简单使用
可以看到,我们使用一个组件首先继承React.Componet,声明方法render(){},render方法顾名思义是用来渲染组件,render直接返回一个html标签,标签里面就是我们要展现的页面内容,这里使用的JSX的语法,如果编辑器不支持JSX语法,需要在设置->Languages->JavaScript中切换。
另外有一点是,这里的组件可以分装嵌套使用,就像java/Android中的组件继承、嵌套是一个概念,非常接近java的语法了,这就是组件化,最大的好处是实现组件的重用。
最后需要把组件绘制到页面中,ReactDOM.render(),把需要渲染的组件和需要渲染的位置传入其中,最后在页面中展示出来。
这里的DOM是react的虚拟DOM,里面的节点既可以包含标签,又可以包含自定义的组件,区别在于html标签小写开头,自定义的大写开头,可以实现快平台。
state--状态
一般来说,你需要在constructor中初始化state
React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。
React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。
不需要直接操作DOM,而是通过修改state,自动更新界面
示例:
lass Text extends React.Component {
//设置初始状态的值
getInitialState() {
return {isLike:false}
}
//响应点击事件
handleClick() {
//改变状态
this.setState({isLike:!this.state.isLike});
}
render() {
var text = this.state.isLike?"确定":"取消"
return <p onClick={this.handleClick}>
这是{text}按钮
</p>
}
}
ReactDOM.render(<Text/>,document.getElementById("mydiv"));
/*Uncaught TypeError: Cannot read property 'isLike' of null*/
这里代码说明:
- 在React中的属性命名遵循小驼峰命名法,不然无法识别,如果上面的onClick
- 通过响应点击事件,调用this.setState()方法给组件的state属性赋值,改变state的值,从而react自动重新调用的render方法重新渲染
- 给state赋值之前需要在组件的生命周期getInitialState()方法给state进行初始化工作,getInitialState在组件创建的时候回调用。
然而这样运行会报错(这里需要安装react的官方chorme的插件):
Uncaught TypeError: Cannot read property 'isLike' of null
这里说isLike为空?说明getInitialState方法没有被调用,其实这里还涉及到另外一个知识:
react创建组件有两种方式,第一种就像上述代码直接继承,第二种是调用React.creatClass()方法,第二种方式才能走生命周期的方法,将代码改为:
var Text = React.createClass( {
//设置初始状态的值
getInitialState:function() {
return {isLike:false}
},
//响应点击事件
handleClick:function() {
//改变状态
this.setState({isLike:!this.state.isLike});
},
render:function() {
var text = this.state.isLike?"确定":"取消"
return <p onClick={this.handleClick}>
这是{text}按钮
</p>
}
})
如果需要用继承的方式个state赋值,需要在构造函数里面,如果是原生的js代码需要操作DOM:$('#id').innerText = "";这样子就不能跨平台了。
props -- 属性
props是在父组件中指定,而且一经指定,在被指定的组件的生命周期中则不再改变。 对于需要改变的数据,我们需要使用state
state 和 props 主要的区别在于 props 是不可变的,而 state 可以根据与用户交互来改变。这就是为什么有些容器组件需要定义 state 来更新和修改数据。 而子组件只能通过 props 来传递数据。
首先看下案例:
var Text = React.createClass({
render : function () {
return <p>{this.props.name}</p>
}
})
ReactDOM.render(<Text name="I Love You"/>,document.getElementById("mydiv"));
通过在引用的时候在组件的标签内命名key:value值,这样传入的值就是赋值给这个组件的默认属性,通过this.props.key来使用,这个属性和java的构造穿参类似。
那怎么理解子组件只能通过props来传递数据呢?试想这样一个场景:父组件包裹子组件,当父组件内容改变的时候子组件跟着要改变,这就需要通过props给子组件串值。
案例:
//子组件
class TextComponet extends React.Component {
render() {
return <div>hello {this.props.text}</div>
}
}
//父组件
var WrapperComponent = React.createClass({
getInitialState:function () {
return {text:""}
},
//e是Event对象,target是目标对象,value是值。
handleChange: function (e) {
// alert(e.target.value);
this.setState({text:e.target.value});
},
render:function () {
return <div>
<TextComponet text={this.state.text}/>
<input type="text" onChange={this.handleChange}/>
</div>
}
})
ReactDOM.render(<WrapperComponent name="I Love You"/>,document.getElementById("mydiv"));
上面案例经历了的步骤:
1、给父组件状态赋初始值
2、输入框变化——>修改父组件的state
3、state变化重新渲染调用render(),子组件也会重新渲染,把父组件的state值作为子组件的props值传递给子组件。
4、子组件的内容改变。
这个方法可以当做一个模板套路来是用。
那么子组件发生变化怎么通知父组件呢?
来个表单提交的例子:
var ChildGenderSelect = React.createClass({
render:function () {
return <select onChange={this.props.handleSelectChange}>
<option value="1">男</option>
<option value="0">女</option>
</select>
}
})
var ParentForm = React.createClass({
getInitialState:function () {
return {gender:0}
},
handleChange: function (e) {
this.setState({gender:e.target.value})
//ajax发起请求
},
handlSubmit:function (e) {
//屏蔽表单的默认行为
e.preventDefault()
var request = new XMLHttpRequest();
request.open("GET","text.json?gender=" + this.state.gender);
request.onreadystatechange = handler;
request.setRequestHeader("Accept","application/json");
request.responseType="json";
request.send();
function handler() {
if (this.status === 200) {
console.log("ok");
}
}
},
render: function () {
return <form onSubmit={this.handlSubmit}>
<ChildGenderSelect handleSelectChange={this.handleChange}/>
<button type="submit">提交</button>
</form>
}
})
ReactDOM.render(<ParentForm/>,document.getElementById("mydiv"));
解析:父组件传递给子组件的props一个父组件的方法handleChange(),当子组件的选择器发生改变的时候通过调用this.props.handleSelectChange(),最终调用父组件的handleChage(),和java的观察者模式思想有点像。
小结:父组件通知子组件变化通过改变父组件的状态,把父组件的状态值当做子组件的props传递进去,当父组件的state发生改变的时候重新调用render方法,从而达到传值的目的,而子组件通知父组件,通过传递父组件方法给子组件的props,当子组件发生改变的时候回调这个方法,和java的观察者很像。
React的生命周期
1.getDefaultProps
作用于组件类,只调用一次,返回对象用于设置默认的props,对于引用值,会在实例中共享。
2.getInitialState
作用于组件的实例,在实例创建时调用一次,用于初始化每个实例的state,此时可以访问this.props。
3.componentWillMount
在完成首次渲染之前调用,此时仍可以修改组件的state。
4.render
必选的方法,创建虚拟DOM,该方法具有特殊的规则:
- 只能通过this.props和this.state访问数据
- 可以返回null、false或任何React组件
- 只能出现一个顶级组件(不能返回数组)
- 不能改变组件的状态
- 不能修改DOM的输出
5.componentDidMount
真实的DOM被渲染出来后调用,在该方法中可通过this.getDOMNode()访问到真实的DOM元素。此时已可以使用其他类库来操作这个DOM。
在服务端中,该方法不会被调用。
6.componentWillReceiveProps
组件接收到新的props时调用,并将其作为参数nextProps使用,此时可以更改组件props及state。
componentWillReceiveProps: function(nextProps) {
if (nextProps.bool) {
this.setState({
bool: true
});
}
}
7.shouldComponentUpdate
组件是否应当渲染新的props或state,返回false表示跳过后续的生命周期方法,通常不需要使用以避免出现bug。在出现应用的瓶颈时,可通过该方法进行适当的优化。
在首次渲染期间或者调用了forceUpdate方法后,该方法不会被调用
8.componentWillUpdate
接收到新的props或者state后,进行渲染之前调用,此时不允许更新props或state。
9.componentDidUpdate
完成渲染新的props或者state后调用,此时可以访问到新的DOM元素。
10.componentWillUnmount
组件被移除之前被调用,可以用于做一些清理工作,在componentDidMount方法中添加的所有任务都需要在该方法中撤销,比如创建的定时器或添加的事件监听器。