React两大组件,三大核心属性,事件处理和函数柯里化

x33g5p2x  于2021-09-26 转载在 React  
字(25.6k)|赞(0)|评价(0)|浏览(294)

入门

相关js库

1.react.js:React核心库。

2.react-dom.js:提供操作DOM的react扩展库。

3.babel.min.js:解析JSX语法代码转为JS代码的库。

注意: 核心库必须在扩展库之前引入

入门示例

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>Hello_React</title>
	</head>
	<body>
		<!-- 准备好一个容器 -->
		<div id="test"></div>
		
	<!-- 引入react核心库 -->	
		<script type="text/javascript" src="./js/react.development.js"></script>
	 <!-- 引入react-dom,用于支持react操作dom -->
	 <script type="text/javascript" src="./js/react-dom.development.js"></script>
	 <!-- 引入babel,用于将jsx转换为js -->
	 <script type="text/javascript" src="./js/babel.min.js"></script>
	 
	 <!-- 此处一定要写babel,不写默认是text/javascript -->
	 <!-- 使用babel用于将jsx转换为js -->
	 <script type="text/babel"> //1.创建虚拟dom //jsx语法 const VDOM=<h1>大忽悠</h1>//此处一定不要写引号,因为不是字符串 //2.渲染虚拟dom到页面 ReactDOM.render(VDOM,document.getElementById("test")); </script>
	</body>
</html>

创建虚拟DOM的两种方式

jsx语法创建虚拟DOM

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>Hello_React</title>
	</head>
	<body>
		<!-- 准备好一个容器 -->
		<div id="test"></div>
		
	<!-- 引入react核心库 -->	
		<script type="text/javascript" src="./js/react.development.js"></script>
	 <!-- 引入react-dom,用于支持react操作dom -->
	 <script type="text/javascript" src="./js/react-dom.development.js"></script>
	 <!-- 引入babel,用于将jsx转换为js -->
	 <script type="text/javascript" src="./js/babel.min.js"></script>
	 
	 <!-- 此处一定要写babel,不写默认是text/javascript -->
	 <!-- 使用babel用于将jsx转换为js -->
	 <script type="text/babel"> //1.创建虚拟dom const VDOM=<h1 id="test">大忽悠</h1>//此处一定不要写引号,因为不是字符串 //2.渲染虚拟dom到页面 ReactDOM.render(VDOM,document.getElementById("test")); </script>
	</body>
</html>

js语法创建虚拟DOM

js创建虚拟dom,就不需要引入babel的js库来解析jsx语法了

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>Hello_React</title>
	</head>
	<body>
		<!-- 准备好一个容器 -->
		<div id="test"></div>
		
	<!-- 引入react核心库 -->	
		<script type="text/javascript" src="./js/react.development.js"></script>
	 <!-- 引入react-dom,用于支持react操作dom -->
	 <script type="text/javascript" src="./js/react-dom.development.js"></script>
	 
	 <!-- 此处一定要写babel,不写默认是text/javascript -->
	 <!-- 使用babel用于将jsx转换为js -->
	 <script type="text/javascript"> //1.创建虚拟dom //标签名,标签属性,标签内容 const VDOM=React.createElement('h1',{id:'title'},'大忽悠'); //2.渲染虚拟dom到页面 ReactDOM.render(VDOM,document.getElementById("test")); </script>
	</body>
</html>

如果需要嵌套标签呢? —那么jsx的优势就出来了,下面看对比

js写法:

<script type="text/javascript"> //1.创建虚拟dom //标签名,标签属性,标签内容 const VDOM=React.createElement('h1',{id:'title'}, React.createElement('span',{id:'heihei'},'大忽悠')); //2.渲染虚拟dom到页面 ReactDOM.render(VDOM,document.getElementById("test")); </script>

jsx写法:

<!-- 引入babel,用于将jsx转换为js -->
	 	 <script type="text/javascript" src="./js/babel.min.js"></script>
		 
	 <script type="text/babel"> //1.创建虚拟dom //标签名,标签属性,标签内容 const VDOM=( <h1 id='test'> <span id='heihei'>大忽悠</span> </h1>) //2.渲染虚拟dom到页面 ReactDOM.render(VDOM,document.getElementById("test")); </script>

babel会把jsx语法写的html标签内容,翻译为js写法,相当于一种语法糖

小总结

1.React提供了一些API来创建一种 “特别” 的一般js对象

const VDOM = React.createElement('xx',{id:'xx'},'xx')

上面创建的就是一个简单的虚拟DOM对象

2.虚拟DOM对象最终都会被React转换为真实的DOM

3.我们编码时基本只需要操作react的虚拟DOM相关数据, react会转换为真实DOM变化而更新界。

  • 虚拟DOM本质是一个Object类型的对象(一般对象)
  • 虚拟DOM比较轻,真实DOM比较重,因为虚拟DOM是React内部在用,无需真实DOM上那么多的属性
  • 虚拟DOM最终会被React转换为真实DOM,呈现在页面上

JSX语法规则

1.全称: JavaScript XML

2.react定义的一种类似于XML的JS扩展语法: JS + XML本质是

React.createElement(component, props, ...children)方法的语法糖

3.作用: 用来简化创建虚拟DOM

1)写法:var ele = < h1 >Hello JSX!< /h1 >

2)注意1:它不是字符串, 也不是HTML/XML标签

3)注意2:它最终产生的就是一个JS对象

4.标签名任意: HTML标签或其它标签

5.标签属性任意: HTML标签属性或其它

6.基本语法规则

1)遇到 <开头的代码, 以标签的语法解析: html同名标签转换为html同名元素, 其它标签需要特别解析

2)遇到以 { 开头的代码,以JS语法解析: 标签中的js表达式必须用{ }包含

<script type="text/babel">
	 const id="dhy";
	 const text="大忽悠和小朋友"
	 const VDOM=(
	 <h1 id={id}>
	 <span id='heihei'>{text}</span>
	 </h1>)
	 //2.渲染虚拟dom到页面
	 ReactDOM.render(VDOM,document.getElementById("test"));
	 </script>

jsx里面如果想使用class属性,不要写class,改用className

class是ES6语法里面定义类的关键字

const VDOM=(
	 <h1 id={id}>
	 <span class='heihei'>{text}</span>
	 </h1>)

const VDOM=(
	 <h1 id={id}>
	 <span className='heihei'>{text}</span>
	 </h1>)

jsx里面内联样式要使用style={{key:value}}的形式去写

const VDOM=(
	 <h1 id={id}>
	<font style={{color:'pink',fontSize:'29px'}}>小朋友</font>
	 </h1>)

虚拟dom必须只有一个根标签

此时最外层的div作为根标签

const VDOM=(
	 <div>
	 <h1 id={id}>
	 <font style={{color:'pink',fontSize:'29px'}}>小朋友</font>
	  </h1>
	  <h1 id={id+'1'}>
	  <font style={{color:'pink',fontSize:'29px'}}>大忽悠</font>
	   </h1>
	 </div>
	 )

标签必须闭合

错误,input标签没有闭合

const VDOM=(
	   <input type='text'>
	 )

正确,标签都闭合了

const VDOM=(
	   <input type='text'/>
	 )

标签首字母

(1):若小写字母开头,则将标签转换为html中同名标签元素,若html中无该标签对应的同名元素,则爆错
(2):若大写字母开头,react就去渲染对应的组件,若组件没有定义,则爆错

小案例

当我们传递给react数组时,react会自动帮助我们进行遍历操作,给js对象,不好使

区分js语句和js表达式

1.表达式: 一个表达式会产生一个值,可以放在任何一个需要值的地方
下面这些都是表达式:
(1) a
(2) a+b
(3) demo(1)
(4) arr.map()
(5) function test(){}
2.语句(代码):
(1)if(){}
(2) for(){}
(3)switch(){case:xxx}

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>Hello_React</title>
	</head>
	<body>
		<!-- 准备好一个容器 -->
		<div id="test"></div>
		
	<!-- 引入react核心库 -->	
		<script type="text/javascript" src="./js/react.development.js"></script>
	 <!-- 引入react-dom,用于支持react操作dom -->
	 <script type="text/javascript" src="./js/react-dom.development.js"></script>
	  <!-- 引入babel,用于将jsx转换为js -->
	 	 <script type="text/javascript" src="./js/babel.min.js"></script>
		 
	 <script type="text/babel"> //模拟一些数据 const data=['dhy','xpy'] //创建虚拟DOM const VDOM= ( <div> <h1>前端js框架列表</h1> <ul> { //返回得到一个新数组 //由react遍历当前得到的新数组 data.map((item,index)=> { //读取遍历当前数组的变量值 //每个li必须有自己的唯一标识,即key对应的值,并且key值不可以重复 return <li key={index}>{item}</li> }) } </ul> </div> ) //2.渲染虚拟dom到页面 ReactDOM.render(VDOM,document.getElementById("test")); </script>
	</body>
</html>

模块与组件、模块化与组件化的理解

JS模块

1.理解:向外提供特定功能的js程序, 一般就是一个js文件

2.为什么要拆成模块:随着业务逻辑增加,代码越来越多且复杂。

3.作用:复用js, 简化js的编写, 提高js运行效率

组件

1.理解:用来实现局部功能效果的代码和资源的集合(html/css/js/image等等)

2.为什么要用组件: 一个界面的功能更复杂

3.作用:复用编码, 简化项目编码, 提高运行效率

模块化

当应用的js都以模块来编写的, 这个应用就是一个模块化的应用

组件化

当应用是以多组件的方式实现, 这个应用就是一个组件化的应用

React面向组件编程

使用React开发者工具调试

定义组件

函数式组件

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>Hello_React</title>
	</head>
	<body>
		<!-- 准备好一个容器 -->
		<div id="test"></div>
		
	<!-- 引入react核心库 -->	
		<script type="text/javascript" src="./js/react.development.js"></script>
	 <!-- 引入react-dom,用于支持react操作dom -->
	 <script type="text/javascript" src="./js/react-dom.development.js"></script>
	  <!-- 引入babel,用于将jsx转换为js -->
	 	 <script type="text/javascript" src="./js/babel.min.js"></script>
		 
	 <script type="text/babel"> //创建函数式组件 //函数组件的名字,首字母大写 function Demo() { //babel编译后,会开启严格模式,因此函数组件中的this指向undefined console.log(this); //必须有返回值 return <h1>函数组件</h1> } //渲染组件到页面 // 函数组件的标签首字母要大写,自闭合 ReactDOM.render(<Demo/>,document.getElementById('test')) </script>
	</body>
</html>

babel编译后,会开启严格模式,因此函数组件中的this指向undefined
原理

1.React解析组件标签,找到了MyComponent组件
2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转换为真实DOM,随后呈现在页面中

注意
  • 函数组件的名字,首字母必须大写
  • 函数组件必须有返回值
  • render渲染时,第一个参数填的是函数组件标签,而不是函数组件的名字

Class类复习

<script type="text/javascript"> //创建一个Person类 class Person { //构造器方法 constructor(name,age) { //构造器中的this执行类的实例对象 this.name=name; this.age=age; } //一般方法 speak() { //speak方法放在了类的原型对象上,供实例使用 //通过Person实例调用speak时,speak中的this就是Person实例 //模板字符串 console.log(`姓名:${this.name},年龄:${this.age}`); } } //创建一个Student类,继承自Person class Stu extends Person { constructor(name,age,grade) { //此处必须调用父类的super方法 super(name,age); this.grade=grade; this.friend='小朋友'; } //重写从父类继承过来的方法 speak() { console.log(`姓名:${this.name},年龄:${this.age} ,年级:${this.grade}`); } } </script>

总结:

  1. 类中的构造器不是必须写的,要对实例进行一些初始化的操作,入添加指定属性时才需要写
  2. 如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须调用的
  3. 类中定义的方法,都是放在了类的原型对象上,供实例去使用

类式组件

//创建类式组件---继承React.Component
	  class MyComponent extends React.Component
	  {
		  //render是放在MyComponent的原型对象上的,供实例使用
		  //this是MyComponent 的实例对象
		  //<==>MyComponent组件实例对象
		 render()
		  {
			  return <h2>类组件</h2>
		  }
	  }
	  //渲染组件到页面
	  ReactDOM.render(<MyComponent/>,document.getElementById('test'));

原理

1.React解析组件标签,找到了MyComponent组件
2.发现组件是使用类定义的,随后调用该类的实例,并通过该实例调用到原型上的render方法
3.将render返回的虚拟DOM转换为真实DOM,随后呈现在页面中

组件实例三大核心属性----state属性,和class组件

react中的事件绑定

需求: 定义一个展示天气信息的组件

1.默认展示天气炎热 或 凉爽

2.点击文字切换天气

常用的两种写法:

class Weather extends React.Component
	  {
		 constructor(props)
		  {
			  super(props)
			  //初始化状态
			  this.state={isHot:false}
		  }
		 render()
		  {
			  //读取状态
			  const {isHot}=this.state
			  return <h2 id="w">今天天气{isHot?'炎热':'凉爽'}</h2>
		  }
	  }
	  //渲染组件到页面
	  ReactDOM.render(<Weather/>,document.getElementById('test'));
	  
	  const w1=document.getElementById('w');
	  w1.addEventListener('click',()=>{
		  console.log('标题被点击了');
	  })
	  
	  const w2=document.getElementById('w');
	  w2.onclick=()=>{
		  console.log('标题被点击了');
	  }

推荐写法:

class Weather extends React.Component
	  {
		 constructor(props)
		  {
			  super(props)
			  //初始化状态
			  this.state={isHot:false}
		  }
		 render()
		  {
			  //读取状态
			  const {isHot}=this.state
			  return <h2 onClick={demo}>今天天气{isHot?'炎热':'凉爽'}</h2>
		  }
	  }
	  //渲染组件到页面
	  ReactDOM.render(<Weather/>,document.getElementById('test'));

	  function demo()
	  {
		  console.log('标题点击')
	  }

const { xxx } = this.state解释

const { xxx } = this.state;

上面的写法是es6的写法,其实就相当于:

const xxx = this.state.xxx

类中定义的方法,已经在局部(方法体内部)开启了严格模式

开启了严格模式,那么方法里面的this为undefined

class A
		 {
			 s()
			 {
				 //s方法放在了类的原型对象上,供实例使用
				 //通过A的实例对象调用s方法时,s中的this就是A的实例
				 //否则为undefined
				 console.log(this)
			 }
		 }
		 const a=new A();
		 let b=a.s
		 b()

直接调用函数,函数中的this默认指向window,如果全局开启了严格模式,或者在函数体内部局部开启了严格模式,那么函数中的this为undefined

例如:

function a(){
		  'use strict'
		  console.log(this);
	  }
	  a();
	  function b(){
	  		  console.log(this);
	  }
	  b();

react中的this问题

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>Hello_React</title>
	</head>
	<body>
		<!-- 准备好一个容器 -->
		<div id="test"></div>
		
	<!-- 引入react核心库 -->	
		<script type="text/javascript" src="./js/react.development.js"></script>
	 <!-- 引入react-dom,用于支持react操作dom -->
	 <script type="text/javascript" src="./js/react-dom.development.js"></script>
	  <!-- 引入babel,用于将jsx转换为js -->
	 	 <script type="text/javascript" src="./js/babel.min.js"></script>
		 
	 <script type="text/babel">
	 // babel也会默认开启严格默认
	  class Weather extends React.Component
	  {
		 constructor(props)
		  {
			  super(props)
			  //初始化状态
			  this.state={isHot:false}
		  }
		 render()
		  {
			  //读取状态
			  const {isHot}=this.state
			  return <h2 onClick={this.changeWeather}>今天天气{isHot?'炎热':'凉爽'}</h2>
		  }
		  changeWeather()
		  {
			  //changeWeather放在weather的原型对象上,供实例使用
			  //由于changeWeather是作为onClick的回调
			  //所以不是通过实例调用的,是直接调用
			  //类中的方法默认开启了局部的严格模式
			  //所以changeWeather中的this为undefined
			  console.log(this)
		  }
	  }
	  //渲染组件到页面
	  ReactDOM.render(<Weather/>,document.getElementById('test'));

	
	 </script>
	</body>
</html>

解决react的类中this指向问题—bind

// babel也会默认开启严格默认
	  class Weather extends React.Component
	  {
		  //构造函数里面的this指向调用的实例对象
		 constructor(props)
		  {
			  super(props)
			  //初始化状态
			  this.state={isHot:false}
			  //解决changeWeather中的this指向问题
			  this.changeWeather=this.changeWeather.bind(this)
		  }
		 render()
		  {
			  //读取状态
			  const {isHot}=this.state
			  //现在当前类上寻找changeWeather方法,找不到再去原型上找
			  return <h2 onClick={this.changeWeather}>今天天气{isHot?'炎热':'凉爽'}</h2>
		  }
		  changeWeather()
		  {
			  //changeWeather放在weather的原型对象上,供实例使用
			  //由于changeWeather是作为onClick的回调
			  //所以不是通过实例调用的,是直接调用
			  //类中的方法默认开启了局部的严格模式
			  //所以changeWeather中的this为undefined
			  console.log(this)
		  }
	  }
bind不会执行方法,而是返回改变this指向后的新方法

实现点击切换效果

严重注意,React中的状态state不可直接更改
//严重注意,状态state不可直接更改
//下面这行就是直接更改,下面是错误的写法
this.state.isHot=!isHot;
调用react里面的setState方法对state属性进行更新,且更新是一种合并,同名替换,不同名合并
// babel也会默认开启严格默认
	  class Weather extends React.Component
	  {
		  //构造函数里面的this指向调用的实例对象
		  //构造器调用一次
		 constructor(props)
		  {
			  super(props)
			  //初始化状态
			  this.state={isHot:false,wind: '微风'}
			  //解决changeWeather中的this指向问题
			  this.changeWeather=this.changeWeather.bind(this)
		  }
		  //render调用1+n次
		  //1是初始化的那次
		  //n是状态更新的次数
		 render()
		  {
			  //读取状态
			  const {isHot,wind}=this.state
			  //现在当前类上寻找changeWeather方法,找不到再去原型上找
			  return <h2 onClick={this.changeWeather}>今天天气{isHot?'炎热':'凉爽'}
			  ,{wind}</h2>
		  }
		  //事件触发几次,就调用几次
		  changeWeather()
		  {
		   //获取原来的isHot的值
		   const isHot=this.state.isHot;
		   //状态必须通过setState进行更新,且更新时一种合并,不是替换
		   this.setState({isHot:!isHot})
		  }
	  }
	  //渲染组件到页面
	  ReactDOM.render(<Weather/>,document.getElementById('test'));

点击后

类组件总结

  • 类组件中的构造器用来初始化状态和解决this指向问题
  • 改变state属性的值,必须调用setState的方法

state的简写方式

类中可以直接写赋值语句,相当于给实例对象增添了一个属性

class A
	  {
		  constructor(name) {
		      this.name=name;
		  }
		  //类中可以直接写赋值语句,相当于给实例对象增添了一个属性
		  //名为a,值为1
		  a=1
	  }
	  const a1=new A("大忽悠");

类中属性是放在实例对象身上的,而方法是放在原型对象身上的

箭头函数没有自己的this, 它的this是继承而来; 默认指向在定义它时所处的对象(宿主对象),箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。

简写代码

<script type="text/babel">
	  class Weather extends React.Component
	  {
	      //初始化状态
		   this.state={isHot:false,wind: '微风'}
		 render()
		  {
			  const {isHot,wind}=this.state
			  return <h2 onClick={this.changeWeather}>今天天气{isHot?'炎热':'凉爽'}
			  ,{wind}</h2>
		  }
		  //自定义方法---要用赋值语句的形式+箭头函数
		  //箭头函数里面的this就是实例对象
		  //changeWeather此时是一个属性,指向一个方法,放在了实例对象上
		  changeWeather=()=>
		  {
		   const isHot=this.state.isHot;
		   this.setState({isHot:!isHot})
		  }
	  }
	  ReactDOM.render(<Weather/>,document.getElementById('test'));
	 </script>

state总结

1.state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)

2.组件被称为"状态机", 通过更新组件的state来更新对应的页面显示(重新渲染组件)

强烈推荐

1.组件中render方法中的this为组件实例对象

2.组件自定义的方法中this为undefined,如何解决?

a)强制绑定this: 通过函数对象的bind()

b)箭头函数

3.状态数据,不能直接修改或更新

组件三大核心属性2: props

效果:

class Person extends React.Component
	  {
		 render()
		  {
	         const {name,age}=this.props;     
			return (
			<ul>
			<li>姓名:{name}</li>
			<li>年龄:{age}</li>
			</ul>
			)  
		  }
	  }
	  ReactDOM.render(<Person name="bob" age="18"/>,document.getElementById('test'));

props理解

1.每个组件对象都会有props(properties的简写)属性

2.组件标签的所有属性都保存在props中

作用

1.通过标签属性从组件外向组件内传递变化的数据

2.注意: 组件内部不要修改props数据

ES6语法中的展开运算符

//展开数组
	 let arr1=[1,2,3]
	 let arr2=[4,5,6]
	 console.log(...arr1)
	 //连接数组
	 let arr3=[...arr1,...arr2]
	  
	  //在函数中使用--只能作为最后一个参数使用
	  function sum(...nums)
	  {
		  return nums.reduce((preValue,curValue)=>{
			return preValue+curValue  
		  },0)//0是初始累加值
	  }
	  console.log(sum(1,2,3))
	  
	  //构造字面量对象时使用展开语法
	  let person={name: 'tom',age: 18}
	  //展开对象必须在{}中使用
	  let per={...person}
	  console.log(per)
	  //合并对象
	  let per3={...person,name: 'jack'}
	  console.log(per3)

等同写法:

//展开对象必须在{}中使用
	  console.log({...person})
	  //合并对象
	  console.log({...person,name: 'jack'})

展开运算符在react中的应用—批量传递props属性

class Person extends React.Component
	  {
		 render()
		  {
	         const {name,age}=this.props;     
			return (
			<ul>
			<li>姓名:{name}</li>
			<li>年龄:{age}</li>
			</ul>
			)  
		  }
	  }
	  const p={name:'大忽悠',age: '18'}
	  //...p只能在标签里面使用,外部使用会报错
	  //babel可以解析标签里面的...p
	  ReactDOM.render(<Person {...p}/>,document.getElementById('test'));

限制标签里面传递属性的类型,非空限制,默认值等…

需要引入prop-types.js文件

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>Hello_React</title>
	</head>
	<body>
		<!-- 准备好一个容器 -->
		<div id="test1"></div>
		<div id="test2"></div>
		<div id="test3"></div>
		
	<!-- 引入react核心库 -->	
		<script type="text/javascript" src="./js/react.development.js"></script>
	 <!-- 引入react-dom,用于支持react操作dom -->
	 <script type="text/javascript" src="./js/react-dom.development.js"></script>
	  <!-- 引入babel,用于将jsx转换为js -->
	 	 <script type="text/javascript" src="./js/babel.min.js"></script>
	 <!-- 引入prop-types,用于对组件标签属性进行限制 -->
	  <script type="text/javascript" src="./js/prop-types.js"></script>
	 <script type="text/babel"> class Person extends React.Component { render() { const {name,age,speak}=this.props; return ( <ul> <li>姓名:{name}</li> <li>年龄:{age}</li> </ul> ) } } //对标签属性进行类型,必要性的限制 Person.propTypes= { //限制name必须传,且为字符串 name:PropTypes.string.isRequired, //限制年龄为数字类型 age:PropTypes.number, //限制speak必须为函数类型 //这里用func表示函数类型,而不是function //因为function是js中的关键字 speak: PropTypes.func } //指定默认标签属性值 //如果标签里面没传对应的属性,那么赋予属性默认值 Person.defaultProps= { age: 18 } function speak() { return "dhy like xpy"; } ReactDOM.render(<Person name="小盆友" age={3} speak={speak}/>,document.getElementById('test1')); ReactDOM.render(<Person name="超级大忽悠"/>,document.getElementById('test2')); const p={name:'大忽悠',age:18} //...p只能在标签里面使用,外部使用会报错 ReactDOM.render(<Person {...p}/>,document.getElementById('test3')); </script>
	</body>
</html>

react中的props是只读的,修改会报错

class Person extends React.Component
	  {
		 render()
		  {
	         const {name,age,speak}=this.props;   
			   //props是只读的,下面写法会报错
		this.props.name="匿名"
			return (
			<ul>
			<li>姓名:{name}</li>
			<li>年龄:{age}</li>
			</ul>
			)  
		  }
	  }

如何给class类自身加上属性

实例对象身上没有b属性,类身上才有

class A
		 {
			 constructor(a) {
			    this.a=a; 
			 }
		  //给实例对象上身上添加属性 
	      a=1;		 
		  static b=2
		 }
		 const a=new A(10);
	    console.log(a);

如何给类身上添加属性:

方法一:

class A
		 {
		  //给实例对象上身上添加属性 
	      a=1;		 
		 }
		 //给class类自身加上属性
	     A.b=2;
		console.log(A);	
	    console.log(A.b)

方法二: static方法

class A
		 {
			 constructor(a) {
			    this.a=a; 
			 }
		  //给实例对象上身上添加属性 
	      a=1;		 
		  static b=2
		 }
		 console.log(A);
	    console.log(A.b);

props的简写方式

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>Hello_React</title>
	</head>
	<body>
		<!-- 准备好一个容器 -->
		<div id="test1"></div>
		<div id="test2"></div>
		<div id="test3"></div>
		
	<!-- 引入react核心库 -->	
		<script type="text/javascript" src="./js/react.development.js"></script>
	 <!-- 引入react-dom,用于支持react操作dom -->
	 <script type="text/javascript" src="./js/react-dom.development.js"></script>
	  <!-- 引入babel,用于将jsx转换为js -->
	 	 <script type="text/javascript" src="./js/babel.min.js"></script>
	 <!-- 引入prop-types,用于对组件标签属性进行限制 -->
	  <script type="text/javascript" src="./js/prop-types.js"></script>
	 <script type="text/babel"> class Person extends React.Component { render() { const {name,age}=this.props; return ( <ul> <li>姓名:{name}</li> <li>年龄:{age}</li> </ul> ) } static propTypes= { name:PropTypes.string.isRequired, age:PropTypes.number } static defaultProps= { age: 18 } } ReactDOM.render(<Person name="小盆友" age={3}/>,document.getElementById('test1')); ReactDOM.render(<Person name="超级大忽悠"/>,document.getElementById('test2')); </script>
	</body>
</html>

类式组件中的构造器与props

如果不初始化 state 或不进行方法绑定,则不需要为 React 组件实现构造函数。

在 React 组件挂载之前,会调用它的构造函数。在为 React.Component 子类实现构造函数时,应在其他语句之前前调用 super(props)。否则,this.props 在构造函数中可能会出现未定义的 bug。

constructor(props)
		  {
			  //构造器是否接受props,是否传递给super,取决于是否希望构造器中调用
			  //this访问props
			  super(props);
			  //如果super(),那么this.props是undefined
			  console.log(this.props)
		  }

函数式组件使用props

//创建函数式组件
   function Per(props)
   {
	 const {name,age}=props
		return (
	   <ul>
	   <li>{name}</li>
	   <li>{age}</li>
	   </ul>
		)	
   }
   Per.propTypes={
	   name:PropTypes.string.isRequired,
       age:PropTypes.number  
   }
   Per.defaultProps=
   {
	  age: 18
   }
   //渲染组件到页面
   ReactDOM.render(<Per name="大忽悠"/>,document.getElementById('test'))

props总结

1.内部读取某个属性值

this.props.name

2.对props中的属性值进行类型限制和必要性限制

第一种方式(React v15.5 开始已弃用):

Person.propTypes = {
 name: React.PropTypes.string.isRequired,
 age: React.PropTypes.number
}

第二种方式(新):使用prop-types库进限制(需要引入prop-types库)

Person.propTypes = {
  name: PropTypes.string.isRequired,
  age: PropTypes.number. 
}

3.扩展属性: 将对象的所有属性通过props传递

<Person {...person}/>

4.默认属性值:

Person.defaultProps = {
  age: 18,
  sex:'男'
}

5.组件类的构造函数

constructor(props){
  super(props)
  console.log(props)//打印所有属性
}

6.props为只读,不可以修改

7.函数式组件可以通过参数获取到props对象,然后进行操作,也可以进行类型和默认值操作

8.PropTypes大写的这个是导入js包后,全局新增的一个对象,而Person.propTypes是我们需要给类上添加的一个属性,react底层会去寻找当前类上名字相同的属性,然后进行遍历,设置对应的类型限制和默认值

组件三大核心属性3: refs与事件处理

  • 组件内的标签可以定义ref属性来标识自己

字符串形式的ref

写在标签里面的是ref,收集出来后形成的属性叫refs

//创建组件
	  class Demo extends React.Component
	  {
		  
		  showData=()=>
		  {
		  //refs里面的input1属性对应input标签的dom对象
			  const{input1}=this.refs;
			  input1.value="大忽悠到此一游";
		  }
		  showData2=()=>
		  {
			  const{input2}=this.refs;
			  input2.value="呜呜呜";
		  }
		  render()
		  {
			  return (
			  <div>
			  <input ref="input1" type="text" placeholder='点击按钮提示数据'/>&nbsp;
			  <button onClick={this.showData}>点我提示左侧数据</button>&nbsp;
			  <input ref="input2"  onBlur={this.showData2} type="text" placeholder='失去焦点,提示数据'/>
			  </div>
			  )
		  }
	  }
	  //渲染组件到页面
	  ReactDOM.render(<Demo/>,document.getElementById('test'));

回调函数形式的ref

//创建组件
	  class Demo extends React.Component
	  {
		  
		  showData=()=>
		  {
			  const{input1}=this;
			  input1.value="大忽悠到此一游";
		  }
		  showData2=()=>
		  {
			  const{input2}=this;
			  input2.value="呜呜呜";
		  }
		  // c就是当前的input标签的dom对象
		  //将其挂载到当前实例上的input1属性上
		  render()
		  {
			  return (
			  <div>
			  <input ref={c=>this.input1=c} type="text" placeholder='点击按钮提示数据'/>&nbsp;
			  <button onClick={this.showData}>点我提示左侧数据</button>&nbsp;
			  <input ref={c=>this.input2=c}  onBlur={this.showData2} type="text" placeholder='失去焦点,提示数据'/>
			  </div>
			  )
		  }
	  }
	  //渲染组件到页面
	  ReactDOM.render(<Demo/>,document.getElementById('test'));

回调ref中调用次数问题

如下的内联函数,每次渲染都会创建一个新的,并且先清空之前的ref,传入null,第二次传入dom元素

<input ref={c=>this.input1=c} type="text" placeholder='点击按钮提示数据'/>

如果是class的绑定函数,那么react就知道当前绑定函数被调用过,不会新创建函数实例,也就不会在更新的时候调用对应的函数了

React 初学 - 回调ref中调用次数的问题 - 个人笔记26

createRef的使用

createRef创建出来的容器,只能放一个dom元素,后放进去的dom元素会覆盖之前放入的dom元素

//创建组件
	  class Demo extends React.Component
	  {
		  //React.createRef调用后返回一个容器
		  //该容器可以存储ref所标识的节点,该容器是专人专用
		  //后来的覆盖之前的
		  myref=React.createRef()
		  showData=()=>
		  {
			console.log(this.myref)
		  }
		  //将input标签放入myref容器中
		  //如果将button也放入myref容器中
		  //那么会覆盖之前的input
		  render()
		  {
			  return (
			  <div>
			  <input ref={this.myref} type="text" placeholder='点击按钮提示数据'/>&nbsp;
			  <button ref={this.myref} onClick={this.showData}>点我提示左侧数据</button>&nbsp;
			  </div>
			  )
		  }
	  }
	  //渲染组件到页面
	  ReactDOM.render(<Demo/>,document.getElementById('test'));

React中的事件处理

1.通过onXxx属性指定事件处理函数(注意大小写)

1)React使用的是自定义(合成)事件, 而不是使用的原生DOM事件----为了更好的兼容性

2)React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)---为了高效

2.通过event.target得到发生事件的DOM元素对象

当发生事件的元素正好是我们需要操作的元素时,ref可以不写,通过event可以获取到对应的dom元素

//创建组件
	  class Demo extends React.Component
	  {
		  showData=(event)=>
		  {
			console.log(event.target)
		  }
		  render()
		  {
			  return (
			  <div>
			  <input ref={this.myref} type="text" placeholder='点击按钮提示数据'/>&nbsp;
			  <button onClick={this.showData}>点我提示左侧数据</button>&nbsp;
			  </div>
			  )
		  }
	  }
	  //渲染组件到页面
	  ReactDOM.render(<Demo/>,document.getElementById('test'));

受控和非受控组件

非受控组件—现用现取

//创建组件
	  class Demo extends React.Component
	  {
		 handleSubmit=(event)=>
		 {
			 //阻止表单提交
			 event.preventDefault()
		     const{nameInput,pwdInput}=this
			 //模板字符串
			 alert(`输入的用户名:${nameInput.value},输入的密码:${pwdInput.value}`)
		 }
		  render()
		  {
			  return(
			 <form onSubmit={this.handleSubmit}>
			 用户名: <input ref={c=>this.nameInput=c} type="text" name="username"/><br/>
			 密码: <input ref={c=>this.pwdInput=c} type="password" name="password"/><br/>
			 <button>登录</button>
			 </form>
			  )
		  }
	  }
	  //渲染组件到页面
	  ReactDOM.render(<Demo/>,document.getElementById('test'));

受控组件—省略ref

//创建组件
	  class Demo extends React.Component
	  {
		  //初始化状态
		  state={
			  name: '',
			  pwd: ''
		  }
		  //保存用户名到状态中
		  saveName=(event)=>
		  {
			  this.setState({name:event.target.value})
		  }
		  //保存密码到状态中
		  savePwd=(event)=>
		  {
		  			  this.setState({pwd:event.target.value})
		  }
		 handleSubmit=(event)=>
		 {
			 //阻止表单提交
			 event.preventDefault()
		     const{name,pwd}=this.state
			 alert(`输入的用户名:${name},输入的密码:${pwd}`)
		 }
		  render()
		  {
			  return(
			 <form onSubmit={this.handleSubmit}>
			 用户名: <input onChange={this.saveName} type="text" name="username"/><br/>
			 密码: <input onChange={this.savePwd} type="password" name="password"/><br/>
			 <button>登录</button>
			 </form>
			  )
		  }
	  }
	  //渲染组件到页面
	  ReactDOM.render(<Demo/>,document.getElementById('test'));

函数柯里化

高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数

  1. 若A函数,接收的参数是一个函数,那么A就可以成为高阶函数
  2. 若A函数,调用的返回值依然是一个函数,那么A就可以成为高阶函数
  • 函数柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式

[]方式读取对象的属性

let a='name'
	  let obj={name:"大忽悠"}
	  console.log(obj[a])
	  //下面是错误写法
	  console.log(obj.a)

//创建组件
	  class Demo extends React.Component
	  {
		  //初始化状态
		  state={
			  name: '',
			  pwd: ''
		  }
        //保存表单数据到状态中
		saveFormData=(dataType)=>{
			return (event)=>
			{
				//[]是读取变量里面的值
				this.setState({[dataType]:event.target.value})
			}
		}
		 handleSubmit=(event)=>
		 {
			 //阻止表单提交
			 event.preventDefault()
		     const{name,pwd}=this.state
			 alert(`输入的用户名:${name},输入的密码:${pwd}`)
		 }
		  render()
		  {
			  return(
			 <form onSubmit={this.handleSubmit}>
			 用户名: <input onChange={this.saveFormData('name')} type="text" name="username"/><br/>
			 密码: <input onChange={this.saveFormData('pwd')} type="password" name="password"/><br/>
			 <button>登录</button>
			 </form>
			  )
		  }
	  }
	  //渲染组件到页面
	  ReactDOM.render(<Demo/>,document.getElementById('test'));

onChange事件这里接收到的是saveFormData函数的返回值,因为saveFormData函数的返回值还是一个函数,onChange调用返回的函数时,会往里面传入一个event对象

不使用函数柯里化的写法

//创建组件
	  class Demo extends React.Component
	  {
		  //初始化状态
		  state={
			  name: '',
			  pwd: ''
		  }
        //保存表单数据到状态中
		saveFormData=(dataType,event)=>{
				//[]是读取变量里面的值
				this.setState({[dataType]:event.target.value})
		}
		 handleSubmit=(event)=>
		 {
			 //阻止表单提交
			 event.preventDefault()
		     const{name,pwd}=this.state
			 alert(`输入的用户名:${name},输入的密码:${pwd}`)
		 }
		  render()
		  {
			  return(
			 <form onSubmit={this.handleSubmit}>
			 用户名: <input onChange={(event)=>{this.saveFormData('name',event)}} type="text" name="username"/><br/>
			 密码: <input onChange={(event)=>{this.saveFormData('pwd',event)}} type="password" name="password"/><br/>
			 <button>登录</button>
			 </form>
			  )
		  }
	  }
	  //渲染组件到页面
	  ReactDOM.render(<Demo/>,document.getElementById('test'));

相关文章

微信公众号

最新文章

更多

目录