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的赋值,并不是必须要写在这里边,拿到外边也是可以的。

所以看来看去似乎就只有一个是必须写的,也就是bindbind的作用可以理解为就是生成一个新的函数,同时修改函数里的this指向。

为什么要这个bind呢,原因是如果不写,那么一开始changeUser函数里的this指向的实际是window对象,在严格模式下就成了undefine,所以这里需要使用bindthis指向我们希望指向的对象。

看起来似乎这一行必不可少,这个构造函数也必不可少,但是实际不是的。

并且,上边这样写是针对一个函数,那么如果有十几个甚至几十个函数呢,也就会造成构造函数中这种代码非常多,这也是不太好的。

在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)的主要内容,如果未能解决你的问题,请参考以下文章

React系统学习2(组件三大核心属性之state)

React系统学习4(組件三大核心属性之refs)

React系统学习4(組件三大核心属性之refs)

React系统学习4(組件三大核心属性之refs)

React系统学习4(組件三大核心属性之refs)

React组件三大核心属性: state、props、refs