我先拉拉对React-DOM
输出那篇文章的知识点,从那篇文章中得知,至今为止我们只会用ReactDOM.render()
去更改覆盖数据来进行视图更新,然而还是在时间的控制下进行变化。
现在我们要做的就是让组件自己跑起来
何为
state
?
State
是一种类似于props
的东西,属于组件元素的属性,但是它是私有的,并且完全被组件所控制,所以State
只会存在于组件中,不是组件的话,可以走开了,并且它是可变的,而prop
s这货是不可变的,这也是为什么State
可以取代props
用来实时更新视图的原因。
当然,State
只是属于类组件的,不是函数式的组件的,它被作为一个类的特性与类组件绑定起来,所以函数式组件要使用State
还需要转换为类组件,不然会很尴尬。
转换的步骤如下:
ES6
的类继承于React.Component
render()
render()
函数中render
中将props
替换为this.props
其实上述步骤大家都懂,不多说了。
State
状态标记数据由于我们的props
定义以后存在不可扩展和不可变性,所以用state
来取代props
进行数据更新变化,我们将一个数据要变为可变的,或者说想让props
上的数据可变,实时更新,可以通过一下两步:
1.在render
函数中,将this.props.date
替换为this.state.date
,如下:
class Cl extends React.Component {
render() {
return (
<div>
Hello, world!
It is {this.state.date.toString()}.
div>
);
}
}
2.为类组件增加一个constructor
函数去初始化this.state
这个属性,如下:
class Cl extends React.Component {
constructor(props) {
super(props);//调用父类的构造函数
this.state = {date: new Date()};
}
render() {
return (
<div>
Hello, world!
It is {this.state.date.toString()}.
div>
);
}
}
这里我们通过super
和props
初始化构造函数
constructor(props) {
super(props);
this.state = {date: new Date()};
}
我们将通过React
挂载和卸载两个功能来实现一次Render
即可更新视图,这两种功能的表现形式分别为componentDidMount
和componentWillUnmount
,具体实例如下:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
}
componentWillUnmount() {
}
render() {
return (
<div>
Hello, world!
It is {this.state.date.toString()}.
div>
);
}
}
componentDidMount
和componentWillUnmount
,这两个方法被俗称为生命挂钩(自己取得,代表着跟组件的创建摧毁有关),componentDidMount
函数会在我们render
一个组件元素渲染到视图上后运行,在这里我们可以建立一个定时器,如下:
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount
则是在对象摧毁时执行,一般用来摧毁我们在componentDidMount
中创建的定时器
componentWillUnmount() {
clearInterval(this.timerID);
}
然后我们的更新就完成了,如下:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {
date: new Date()
};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
Hello, world!
It is {this.state.date.toString()}.
div>
);
}
}
ReactDOM.render(
,
document.getElementById('root')
);
看到这些代码,大家就可以知道state
是实时更新的,如此来更新界面,而去掉了反复用render
处理。
这里我们整理一下,上面这份代码的函数调用步骤和实现方式。
当我们的ReactDOM.render()
处理到了Clock
类组件JSX
时,React
会调用Clock
组件的构造函数constructor
,用于给this.state
数据进行初始化
React
会调用Clock
的render
方法,让React
通过这个函数知道怎么将数据渲染到视图中,然后将数据渲染到DOM
上
当React
将Clock
组件的数据内嵌到DOM
中后,React
就会调用componentDidMount()
生命周期钩,就componentDidMount
中的代码而言,React
会告诉浏览器,Clock
类组件需要设定一个定时器,每秒钟调用一次tick
浏览器会每一秒钟调用tick
函数,在tick
中有一个setState
函数,这个的作用就是告诉React
,state
对象被改变了,然后会自动调用render
函数进行重新渲染,也就是说依旧是调用render
,在上面的代码中,React
自己隐性调用的,而不是我们显性调用它。
如果Clock
组件从DOM
中移除的话,React
会调用componentWillUnmount()
来将这个定时器给摧毁掉
state
状态标记属性很明显,state
是一个属性,也可以看成一个对象,那么一定可以进行如下设置:
this.state.date = new Date();
但是这样会非常费神,而且会造成代码冗余,为什么呢,感同身受的就是为一个标签设置css
样式,如果没有jquery
的css
函数,或者是你没有写过一个比较实用的css
处理函数,会发现一个非常懵逼的问题,就是属性设置写的代码能亮瞎你的眼,简直丑的一比。
所以我们这里要用jquery.css
函数的使用方法为state
进行设置。
this.setState({date: new Date()});
state
更新可能会很飘为何这么说呢?但我们一起使用多个setState
,state
的状态就会有点飞。
就是说state
状态对象更新可能是异步的,不是同步的。
当用如下这种形式:就会出现异步情况:
this.setState({
counter: this.state.counter + this.props.increment,
});
上述的操作,如果出现多条的话,会造成计算失误,也就是说多个语句执行的顺序会不同从而造成结果错误。
解决方案
用函数来代替对象处理:
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
//------------------------------------
this.setState(function(prevState, props) {
return {
counter: prevState.counter + props.increment
};
});
这个函数格式是固定的,必须第一个参数是state
的前一个状态,第二个参数是属性对象props
,这两个对象setState
会自动传递到函数中去,同时,这些函数在setState
中的执行是同步的,从而解决了异步问题。
组件之间耦合性越低越好,换句话说,不管是父组件还是子组件,都不知道对方是个什么鬼,不知道对方是函数式组件还是类组件,每一个组件都自我封装,只留下一个借口进行对外交互,同时只有父组件知道子组件会干啥,需要传递什么东西给子组件,而子组件却不知道父组件干了什么,可能传递了props
,或者state
又或是直接明了的数据,他只进行处理数据,不管数据到底怎么来的。
上述这种透明形式,官方成为自上而下,单向数据流,上层只能控制下一层的数据,无法对上层产生任何影响。
简单地说,水往低处流。
此篇博客设计的ES6
的箭头函数如果大家不是很明白可以参考官方网站,当然之后,个人可能会一套ES6
学习的博客,敬请期待
下一篇会将
React
的事件处理机制