React系统学习2(组件三大核心属性之state)
Posted 程序员涂小哥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React系统学习2(组件三大核心属性之state)相关的知识,希望对你有一定的参考价值。
之前简单学习的react中函数式组件和类式组件的基本使用,函数式组件适用于定义简单的组件,类式组件适用于定义复杂的组件。
复杂的组件指的是有状态的组件,所谓的状态,在react中就是三大核心属性之一的state。
从一个小例子说起
假设需要这个一样组件,访问的时候展示一个人的名字和心情,当点击的时候就切换到另一个人,再次点击就再切换回去。
那么对这个需求进行分析,就会知道首先肯定是需要有地方进行信息展示,然后就是肯定需要有点击事件,再然后就是需要有地方保存数据。
基于上边的分析,在还不是太会react的情况下,基于之前的js基础,这个组件一开始可能会先做如下这样的定义:
<body>
<div id="test"></div>
<!--核心库-->
<script type="text/javascript" src="../../js/react.development.js"></script>
<!--操作dom-->
<script type="text/javascript" src="../../js/react-dom.development.js"></script>
<!--用户将jsx转为js-->
<script type="text/javascript" src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
<script type="text/babel">
// 1.类式组件
class ClassComponent extends React.Component
name='tuzongxun'
mood='happy'
render()
return <h1 onclick="changeUser()">I'm this.name,today i'm this.mood</h1>
changeUser()
if(this.name === 'tuzongxun')
this.name='tzx'
this.mood='unhappy'
else
this.name='tuzongxun'
this.mood='happy'
// 2.渲染虚拟DOM
ReactDOM.render(<ClassComponent/>,document.getElementById('test'))
</script>
</body>
然后当在浏览器访问的时候,就会发现点击根本是没有反应的,如果打开浏览器控制台,就会看到有如下的错误提示:
Warning: Invalid event handler property `onclick`. Did you mean `onClick`?
in h1 (created by ClassComponent)
in ClassComponent
很显然,意思是上边的onclick
用法不对,在react中应该是onClick
,其实就是on
后边的c
大写。
类似onclick
这种事件,在react中都需要这样写。
但是,上边代码的问题却不仅仅是这样,当把onclick
改为onClick
后,会看到控制台会有新的错误信息:
Warning: Expected `onClick` listener to be a function, instead got a value of `string` type.
in h1 (created by ClassComponent)
in ClassComponent
这句也比较好理解,就是说onClick
后边应该是一个函数,但是这里却是一个字符串。
根据之前react的基础知识可以知道,这里应该是要把引号改成大括号,之后代码就成了这样:
render()
return <h1 onClick=changeUser()>I'm this.name,today i'm this.mood</h1>
然而,这样改了之后会发现继续报错:
Uncaught ReferenceError: changeUser is not defined
at ClassComponent.render (<anonymous>:34:28)
实际上,这里正确的写法应该是onClick=this.changeUser
,为什么不能加括号呢,是因为加了括号相当于调用了函数,也就相当于把changeUser
函数的返回值赋值给了onClick
事件,而不是函数本身。
上边给出了onClick
定义的正确写法,但是实际上这个程序依旧不行,初始访问正常,当点击的时候就会发现又有了新的异常:
Uncaught TypeError: Cannot read property 'name' of undefined
at changeUser (<anonymous>:44:22)
这又是为什么呢?我的代码里明明是用了this的,难道这个this不是我这个组件的实例对象吗?
实际上,确实不是。
如果在changeUser
函数中打印一下this
,就会看到打印的就是undefined
。
可是,这又是为什么呢?
其实原因也很简单,是因为在事件这种定义中,虽然看起来是this.changeUser
,但是实际是先把changeUser
函数给了onClick
事件,那么当点击的时候,这就变成了函数直接调用,而不是实例进行的调用。
直接调用,就相当于是在外边的js
中直接写了changeUser()
,而这时候,里边的this
实际是window
对象,但是由于react中会使用严格模式,就有不允许直接使用window对象,因此这个this
也就变成了undefined
。
要解决这个问题,就需要在构造函数中使用bind
,即:
constructor()
super()
this.changeUser=this.changeUser.bind(this)
这样改完之后,整个js中的代码就成了这样:
<script type="text/babel">
class ClassComponent extends React.Component
name='tuzongxun'
mood='happy'
constructor()
super()
this.changeUser=this.changeUser.bind(this)
render()
return <h1 onClick=this.changeUser>I'm this.name,today i'm this.mood</h1>
changeUser()
console.log(this)
if(this.name === 'tuzongxun')
this.name='tzx'
this.mood='unhappy'
else
this.name='tuzongxun'
this.mood='happy'
ReactDOM.render(<ClassComponent/>,document.getElementById('test'))
</script>
浏览器访问不在报错,鼠标点击也不在报错。
然而,虽然是不报错,却会发现点击的时候根本就没有反应。
但是打开控制台的输出,却又可以看到输出的this
确实已经被修改成功。
也就是说,我修改的数据,但是react并没有为我重新进行渲染。
怎么办呢?这就要轮到主角state
上场了。
state基础用法
要让react在数据改变好自动渲染页面,需要把相应数据放在state
中,也就是状态驱动渲染,主要涉及到两个方面,一个是数据的存储,另一个是数据的修改。
进行修改后的组件定义如下:
class ClassComponent extends React.Component
constructor()
super();
this.state=name:'tuzongxun',mood:'happy'
this.changeUser=this.changeUser.bind(this);
render()
return <h1 onClick=this.changeUser>I'm this.state.name,today i'm this.state.mood</h1>
changeUser()
if(this.state.name === 'tuzongxun')
this.setState(name:'tzx',mood:'unhappy')
else
this.setState(name:'tuzongxun',mood:'happy')
上边代码中修改的地方主要有两个,一个是把初始数据的定义放在了构造方法中,同时用上了state
,另一个是在changeUser
函数中改变了数据修改的方式。
数据存储
上边初始数据的定义,可以理解为就是数据的存储,之前是直接写在类里边,现在是换成了this.state=name:'tuzongxun',mood:'happy'
.
需要注意的是,state
里边必须是一个对象。
数据修改
之前的问题就是虽然看起来数据每次都修改成功了,但是页面并没有变化,原因就是修改数据的方式不符合react的要求。
如果要让react在数据修改后就渲染页面,就必须用到setState
函数进行数据的修改,这个函数定义在React.Component
中。
实际上这个也很好理解,几乎所有自动化的东西,背后必然都是规则,只有遵循相应的规则了,才能被识别并自动化处理。
那么react
自动渲染的数据修改的方式,也就是类似上边代码里的this.setState(name:'tzx',mood:'unhappy')
state简化写法
上边的代码从功能上讲是没有问题的,但是看起来有些繁琐,尤其是里边的构造方法,其实是不得已才写的。
构造方法中的super()
就不说了,这是js中类定义的基本规则。
而对state的赋值,并不是必须要写在这里边,拿到外边也是可以的。
所以看来看去似乎就只有一个是必须写的,也就是bind
,bind
的作用可以理解为就是生成一个新的函数,同时修改函数里的this
指向。
为什么要这个bind
呢,原因是如果不写,那么一开始changeUser
函数里的this
指向的实际是window
对象,在严格模式下就成了undefine
,所以这里需要使用bind
把this
指向我们希望指向的对象。
看起来似乎这一行必不可少,这个构造函数也必不可少,但是实际不是的。
并且,上边这样写是针对一个函数,那么如果有十几个甚至几十个函数呢,也就会造成构造函数中这种代码非常多,这也是不太好的。
在js中有一种写法叫箭头函数,其实也就是相当于java
中的lambda
表达式。
箭头函数本身是没有this
的,里边如果有this
,则会指向函数定义的外侧this
。
因此,进一步简写后,上边组件的定义则可以改成这样:
class ClassComponent extends React.Component
state=name:'tuzongxun', mood:'happy'
render()
return <h1 onClick=this.changeUser>I'm this.state.name,today i'm this.state.mood</h1>
changeUser = ()=>
console.log(this)
if(this.state.name === 'tuzongxun')
this.setState(name:'tzx',mood:'unhappy')
else
this.setState(name:'tuzongxun',mood:'happy')
js中绑定事件的三种方式
在上边的例子中,使用到了点击事件,实际上js中点击事件的写法至少有三种,上边的写法只是其中一个,由于简洁,可能也是最常用的一个。
既然知道至少有三种方式,那么这里就顺便对这三种做一个简单记录:
<body>
<button id="btn1">button1</button>
<button id="btn2">button2</button>
<button onclick="click3()">button3</button>
<script type="text/javascript">
var button1=document.getElementById('btn1')
button1.addEventListener('click',()=>
alert("click button1")
);
var button2=document.getElementById('btn2')
button2.onclick=()=>
alert("click button2")
function click3()
alert("click button3")
</script>
</body>
以上是关于React系统学习2(组件三大核心属性之state)的主要内容,如果未能解决你的问题,请参考以下文章