此文章是本人在学习React的时候,写下的学习笔记,在此纪录和分享。此为第四篇,主要介绍react中的state。
1.state(状态)
1.1state简介
存在state(状态)的组件称为复杂组件,反之称为简单组件。
何为状态呢?举例,人是有状态的,状态影响着人的行为举止。组件也是有状态的,数据存放在状态内,状态驱动着页面的展示。
1.state是组件对象中最重要的属性,值是对象(可以包含多个key-value的组合)
2.组件被称为“状态机”,通过更新组件的state来更新对应的页面显示(重新渲染组件)
3.在类定义的组件中,state是其三大核心属性之一,另外两个是props,refs。
1.2案例:炎热与凉爽的切换
1.2.1非实时切换
我们先创建类式组件Weather,在reander函数内打印一下this指向。
如图所示,我们打印出了Weather这个实例对象,在实例对象内存在state,而且state是一个对象。如果我们没有设置state,那么它就是一个空对象,在代码内我们写了:this.state = {isHot:true},所以现在state内就有了isHot:true这条数据。
在render函数内的构建虚拟dom中,我们使用三目运算符,根据this.state.isHot是true还是false,来分别显示炎热或者凉爽。
代码:
<!-- 准备好容器 -->
<div id="test"></div>
<!-- 引入react库,以下依次为核心库,操作dom的库和babel(将jsx转为js的库),此顺序不可有错误 -->
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
<script type="text/babel">//写为babel格式,否则识别为普通js文件
class Weather extends React.Component {
constructor(props){//使用构造器和super关键字,从父类继承props
super(props)
this.state = {isHot:true}//为实例中的State
}
render() {
console.log(this);
return <h1>今天天气很{this.state.isHot?'炎热':'凉爽'}</h1>
}
}
ReactDOM.render(<Weather/>, document.getElementById('test'))
</script>
显示结果:
如果我们手动设置this.state = {isHot:false}
结果:
1.2.2React中的为元素绑定事件
现在我们要实现,炎热和凉爽的动态切换,首先要对元素绑定事件。众所周知,原生js中为元素添加事件的办法有三种,元素.onclick,元素.addEventListener(’事件名字‘,函数体)和<div οnclick='demo()'>按钮</div>。我们使用第三种,但注意写成<div onClick={demo}>按钮</div>,在react中事件名称要写成小驼峰的形式,并且函数名后不可以加小括号,否则会直接调用函数,在dom渲染在界面的时候直接把函数的返回值跟着一起渲染一次。
代码更改部分如下:
class Weather extends React.Component {
constructor(props){//使用构造器和super关键字,从父类继承props
super(props)
this.state = {isHot:false}//为实例中的State
}
render() {
console.log(this);
//为h1标签添加点击事件和点击后触发的函数demo
return <h1 onClick={demo}>今天天气很{this.state.isHot?'炎热':'凉爽'}</h1>
}
}
ReactDOM.render(<Weather/>, document.getElementById('test'))
function demo(){//点击事件的函数
console.log('此标签被点击');
}
效果如下:
1.2.3实现动态切换
学习react,我们对原型,类,实例对象和this指向这部分知识,应该掌握牢固。
首先我们要知道state(状态)不可以直接的更改,要借助一个内置的api--setState去更改,如果我们直接去更改。如:this.state.isHot = !isHot这种写法,是没有作用的。必this.setState({isHot:!isHot})这样写,才能有效果。而且此api更新state是合并,而并非覆盖,不会丢失其他原有数据。
其次我们在构造器中,在Weather原型链上寻找changeWeather这个函数,并改变其this指向,赋值给demo使其存在在Weather的实例对象上,也就是this.demo=this.changeWeather.bind(this)这行代码,这样在rendr中虚拟dom添加点击事件的回调函数中,使用this.demo就可以直接在触发点击时调用我们放在实例对象上的changeWeather函数,就不会去沿着原型链去寻找changeWeather,直接选择本实例对象上的changeWeather,解决了类中系统自动开启局部严格模式导致this指向underfined,调用不到changeWeather的情况。
最后是构造器,render和changeWeather函数调用几次的问题。在创建的类实例化的时候,构造器会调用一次,之后均不会被调用。此时render去生成虚拟dom被调用一次,数据更新时render均会被调用一次。changeWeather这个回调函数控制数据的更新,所以每次我们触发点击事件,state状态进行更新,那么changeWeather就会被调用。
代码如下:
class Weather extends React.Component {
//构造器调用,仅仅1次
constructor(props){//使用构造器和super关键字,从父类继承props
super(props)
//初始化状态
this.state = {isHot:false}//为实例中的State
//在Weather原型链上寻找changeWeather这个函数,并改变其this指向,赋值给demo使其存在在Weather的实例对象上
this.demo = this.changeWeather.bind(this)
}
//render调用 1+n次,1次是初始化那次,n次是数据更新了几次
render() {
//结构赋值
const {isHot} = this.state
//为h1标签添加点击事件和相应的回调函数changeWeather
//此处写成this.demo,直接调用实例对象上的changeWeather函数
return <h1 onClick={this.demo}>今天天气很{isHot?'炎热':'凉爽'}</h1>
}
//changeWeather是,点几次它就调用几次
changeWeather(){
//changeWeather放在Weather的原型对象上,供实例调用
//由于changeWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用
//类中的方法默认开启了局部的严格模式,所以changeWeather中的this为underfined
const isHot = this.state.isHot
//状态不可以直接的更改,要借助一个内置的api(setState)去更改
// 这行就是直接更改:this.state.isHot = !isHot
this.setState({isHot:!isHot})//使用setState来更改状态,且更新是一种合并不是替换,不会丢失原有数据
}
}
ReactDOM.render(<Weather/>, document.getElementById('test'))
效果就是点击文字,炎热和凉爽二字会来回切换。
1.3state代码精简
上面的代码写的实在是太繁琐了,在使用react时用不着那么麻烦,所以我们对上面的代码进行精简书写。
首先我们去掉构造器,直接在类里面写赋值语句,与在构造器中写一样,直接在实例对象中存在。
然后我们把changeWeather使用箭头函数属性,并且赋值给changeWeather。箭头函数没有自己的this,在涉及this指向时候,其this和其外侧函数一致,所以下方的this.setState({isHot:!isHot})中的this直接是指向Weather的实例对象,保证直接更改实例对象中的state。
代码如下:
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
<script type="text/babel">
class Weather extends React.Component {
state = {isHot:false}//直接在类里面写赋值语句,与在构造器中写一样
render() {
const {isHot} = this.state
return <h1 onClick={this.demo}>今天天气很{isHot?'炎热':'凉爽'}</h1>
}
//自定义函数,必须使用赋值语句和箭头函数
changeWeather = ()=>{//此处必须使用箭头函数,箭头函数没有自己的this,在涉及this指向时候,其this和其外侧函数一致
const isHot = this.state.isHot
this.setState({isHot:!isHot})//所以此处this指向,是Weather的实例对象
}
}
ReactDOM.render(<Weather/>, document.getElementById('test'))
</script>
1.4state总结
1.state是组件对象中最重要的属性,值是对象(可以包含多个key-value的组合)
2.组件被称为“状态机”,通过更新组件的state来更新对应的页面显示(重新渲染组件)
3.在类定义的组件中,state是其三大核心属性之一,另外两个是props,refs。
强烈注意:
1.组件中render方法中的this为组件实例对象。
2.如何解决组件内自定义方法this指向为undefined?
(1),强制绑定this,通过函数对象的bind
(2),使用箭头函数
3.状态数据,不能直接修改或者更新,使用一个内置的api(setState)去更改