版权声明:本文为博主原创文章,未经博主允许不得转载。
PS:转载请注明出处
作者:TigerChain
地址:http://www.jianshu.com/p/44a787904d9b
本文出自TigerChain简书
教程简介
- 1、阅读对象
本篇教程适合新手阅读,老手直接略过
-
2、教程难度
初级
3、 本节 demo 的地址
https://github.com/tigerchain/react-lesson/tree/master/lesson02/09-state 如果大家喜欢,给个 star 鼓励一下
正文
注:本篇文章采用ES6的写法,以后不没有特别说明都使用ES6+yarn+webpack去写demo
1、什么是state
我们都说React是一个状态机,体现是什么地方呢,就是体现在state上,通过与用户的交互,实现不同的状态,然后去渲染UI,这样就让用户的数据和界面保持一致了。state是组件的私有属性。
在React中,更新组件的state,结果就会重新渲染用户界面(不需要操作DOM),一句话就是说,用户的界面会随着状态的改变而改变。
PS: state 只能在本组件中去初始化,并且 state 只能被本组件去修改和仿问,不能被外部仿问和修改,所以也可以说,state 是组件私有的。
2、state的使用方法
1、初始化state
对于state的使用方法,在ES5中和Es6中使用是不一样的,我们以ES6为例,在组件的构方法中如下设置即可,设置一个默认的state属性。
constructor(props) {
super(props);
this.state={
key:value,
...
}
}
2、更新state
更新state调用以下方法
this.setState({
key:value
}) ;
千万不要这么干,this.state.key = XXX ; 这样不会重新渲染界面
注意:setState()是异步的,也就是你调用了setState()之后,React就开始准备去更新了,中间计算会可能会有一定的延时。
PS:如果调用了setState()方法以后,就会调用render()方法,也就是前面说的,用户的界面会随着状态的改变而改变。
3、调用diff算法
这一步是在2步的基础上的,setState()会触发diff算法最终确定是否要更新。
3、什么样的组件应该有state
应该说大部分的组件根据props来取得数据并渲染,但是用户输入,请求服务器,操作数据库等情况下就需要state了.官方的建议是尽量构建无状态的组件,是为了提高性能,减少渲染次数,做法就是构建几个无状态的组件,在这之上构建一个有状态和用户和服务交互 ,然后通过props来传递给无状态的组件。
使用state要注意三个事情
- 1、不要直接的修改state
反例 请不要这样做
this.state.comment = 'Hello';
正确的做法是使用setState()方法
正确的做法
this.setState({
comment:'Hello',
})
- 2、状态更新是异步的
用官方的话说就是this.props和this.state更新的时候可能是异步的,所以你不应该依靠他们的值来计算下一个状态
以下是错误的例子
反例
this.setState({
counter: this.state.counter + this.props.increment,
});
正确的例子
正确的例子
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}))
- 3、state的更新是合并后的
当你调用 setState() ,React会合并你提供的对象到当前的 state 里
比如:你的state可能包含几个独立的变量
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
然后你可以通过调用setState()来独立的更新他们
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
这个合并是浅合并,所以 this.setState({comments}) 会让 this.state.posts 完整,但是会完全替换掉 this.state.comments .
4、开始撸码
说一千,道一万,都是在理论级别,下面我们从0开始写一个简单的Demo来体验一下state。下面使用webpack+yarn来写一个demo。
在mac环境下,win下基本差不太多
1、在指定目录下新建state目录(命令行中操作)
mkdir state
2、使用yarn初始化项目(命令行中操作)
cd state
yarn init
在上面步骤一路回车即可
3、安装一些依赖(命令行中操作)
yarn add react react-dom webpack webpack-dev-server babel-core babel-loader babel-preset-react bable-preset-es2015 babel-preset-stage-0 --dev
然后回车,等待安装依赖,如果没有什么问题就可以在package.json中看到如下结果(红色框中)
以上就表明你的依赖安装成功了
4、在state根目录中新建.babelrc并输入以下内容
{
"presets":["react","es2015","stage-0"]
}
5、在state目录中分别新建app和public文件夹
6、在public中新建index.html并输入以下内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Hello state</title>
</head>
<body>
<div id="container"></div>
<script src="bundle.js"></script>
</body>
</html>
7、在app目录中分别新建main.js和State.js
# main.js
import React from 'react' ;
import ReactDOM from 'react-dom' ;
import State from './State.js' ;
ReactDOM.render(
<State />,
document.getElementById('container')
) ;
# State.js
import React, { Component, PropTypes } from 'react';
/**
* 使用es6语法 定义一个State组件
*/
export default class State extends Component {
constructor(props) {
super(props);
this.state = { //初始化state
countnum:0,
};
}
/**
* 点击事件方法 countnum+1
*/
_handlerEvent(){
this.setState({
countnum:this.state.countnum+1,
})
}
render() {
return (<div>
{this._renderView()}
</div>);
}
/**
* 渲染一个button组件
*/
_renderView(){
return(
<div>
<button onClick={this._handlerEvent.bind(this)}>点击{this.state.countnum}次</button>
</div>
);
}
}
8、在state根目录中新建webpack.config.js并且输入以下内容,具体不清楚的可以看webpack这一节:http://www.jianshu.com/p/732c4d501668
module.exports = {
entry:__dirname+'/app/main.js',
output:{
path:__dirname+'/public',
filename:'bundle.js'
},
devServer:{
contentBase: "./public",//本地服务器所加载的页面所在的目录
historyApiFallback: true,//不跳转
inline: true//实时刷新
},
module:{
loaders:[
//babel配置
{
test:/\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
}
]
}
}
9、在package.json中添加脚本(这不是必须的,但是可以方便调用)
图中的蓝色部分就是我们添加的脚本,即:
"scripts":{
"start":"webpack-dev-server --progress --port 8888"
}
10、在命令行直接运行 yarn start 如果没有什么问题,我们在浏览器上输入localhost:8888,会看到下面的结果
以上我们就完成了一个简单的state的例子,从这个例子中我们可以看到每当我点击按钮的时候组件的状态都会发生改变,计数就加1了,但是当我们调用了setState()方法到底有没有刷新界面呢?我们再测试一下
11、接着上面的例子我们继续修改State.js
从图中可知我们只是在原有的基础上添加了现代战争方法,用来测试render方法调用次数,然后保存,看结果(打开chrome的调试栏)
从图中我们可以清楚的看到,我们点击了多少次,render方法就调用了多少次,所以说当调用了setState()方法就会重新渲染界面
到此为止,我们对state就有一个大体的认知了。下面我们来说一个无状态组件的例子。
5、无状态的组件
1、什么是无状态组件
无状态组件顾名思义就是没有状态的组件我们上面提到过无状态的组件,官方建议的是尽量让组件无状态化,但是肯定有一个地方要状态化的,一般做法是把要组合的组件无状态化,宿主组件有一个状态设置(一般情况下,也就是父组件更新子组件的时候,但是有时会反过来)
接下来我们构建一个无状态的组件,然后通过和用户交互(点击)的时候把状态通过props传给无状态的组件达到更新UI的目的,这么说可能有点绕我们看下面图来直观的理解一下
上图就是这一过程的一个伪代码,很好理解。
2、我们通过一个demo来直观感受一下
1、接着上面的例子,我们继续在app中分别新建PassStateOfprops.js和StateLess.js
# PassStateOfprops.js 此组件可以当作有状态的父组件
import React, { Component, PropTypes } from 'react';
import StateLess from './StateLess.js' ;
/**
* 通过 props来传递state 达到更新组件的目的
*/
export default class PassStateOfprops extends Component {
constructor(props) {
super(props);
this.state = { //初始化state
list:[
'111',
'222',
'333'
]
}
}
/**
* 渲染界面
*/
render() {
return (<div>
{this._renderButton()}
{/* 将state通过props传递到无状态组件StateLess中 */}
<StateLess datas={this.state.list}/>
</div>);
}
/**
* 渲染一个button
*/
_renderButton(){
return(
<div>
<button onClick={this._handlerEvent.bind(this)}>改变值</button>
</div>
) ;
}
/**
* 点击事件
*/
_handlerEvent(){
this.setState({
list:['444','555','666']
}) ;
}
}
# StateLess.js 定义一个无状态的组件
import React, { Component, PropTypes } from 'react';
/**
* 定义一个无状态的组件
*/
export default class StateLess extends Component {
render() {
return (<div>
<ul>
{
this.props.datas.map(function (data) {
return (
<li>{data}</li>
)
})
}
</ul>
</div>);
}
}
2、修改main.js中代码
其中黄色部分就是我们修改的部分.
3、查看结果
我们看到通过props传递state到无状态的组件成功运行了,所以我们以后在项目中开发,尽量使用无状态化的组件,把这种思想和习惯从现在就养成。
本demo的地址
https://github.com/tigerchain/react-lesson/tree/master/lesson02/09-state