总结
一、React组件
1.1 函数组件
-
定义
要求组件名称的首字母需要大写
function 组件名称(){ // 返回一段jsx的视图结构 return <div></div> }
-
调用
-
<组件名称></组件名称>
或
-
<组件名称 />
-
- 组件名首字母必须大写. 因为react以此来区分组件元素/标签 和 一般元素/标签
- 组件内部如果有多个标签,必须使用一个根标签包裹.只能有一个根标签
- 必须有返回值.返回的内容就是组件呈现的结构, 如果返回值为 null,表示不渲染任何内容
- 会在组件标签渲染时调用, 但不会产生实例对象, 没有状态
1.2 类组件
- 定义
class 类组件名 extends React.Component{
render(){
return <div></div>
}
}
-
解释
- extends:继承
- React.Component: react提供的一个类组件的基础类(父类)
-
注意
- render必须是一个方法
- return的必须是组件类的视图结构
-
特点
- 类组件有自己的生命周期和状态数据
-
调用
-
<组件名称></组件名称>
或
-
<组件名称 />
-
* 使用类式组件的注意事项:
* 1、组件名首字母大写
* 2、每一个类式组件的继承父类都是React.Component
* 3、类式组件中必须要有render方法,通过render方法来进行返回虚拟DOM元素对象
* 4、类式组件调用时相当于ES6中的new 类()的写法,产生组件实例化对象,
* 在组件实例化对象中可以使用像state、props、ref等属性
* 5、在return返回的虚拟DOM元素对象中必须要有一个绝对唯一的根标签,
如果在return后面换行书写,需要添加一个()
* 6、想要给组件实例化对象身上添加初始化的属性,则可以使用constructor构造器,
如果没有明确写出,则系统会默认分配一个空的构造器
如果明确写出构造器,则在构造器中的第一句话需要调用super()方法
* 7、如果类中既有constructor,又有render方法,
则先执行constructor,在执行render,且这两个方法都是自动执行
* 8、其中的this均指向类组件的实例化对象
二、React组件实例的三大核心属性
2.1 state
值是一个对象(可以包含多个key-value)
基本使用
-
初始化 state 两种方法
- 构造器中:
this.state = {xxx: 2}
- 类体中:
state = {xxx: 2}
- 构造器中:
-
读取state数据
- this.state.xxx
-
更新state数据
- 不能直接更新state数据
- 必须
this.setState({ 要修改的属性数据 })
异步方法
-
state中的事件绑定
注意:
在虚拟DOM元素身上绑定事件的时候,事件名遵循小驼峰写法。
事件函数直接使用{函数名}的写法,不能像原生JS中写入一个字符串,
而且也不要在{}中直接调用函数,这样会脱离事件,页面加载后直接运行函数内容。
- 类中方法的this
问题: 类中定义的事件回调方法中
this
是undefined
, 无法更新state原因:
事件回调都不是组件对象调用的, 都是事件触发后,直接调用的,
class中所有方法都使用严格模式, 所以方法中的this就是undefined
解决办法1 - 包裹箭头函数
原因: render中的this是组件对象, 处理函数是我们通过组件对象来调用的解决办法2 - bind绑定this
原因: 构造器中的this是组件对象, 将处理函数通过bind绑定为了组件对象解决办法3 - 箭头函数
原理: 利用bind给事件回调绑定this为组件对象(render中的this)
选择:
- 一般用
箭头函数
方式, 编码简洁- 如果要传递特定的数据, 选择用
包裹箭头函数
方式
问题1:如果state中的不光有isGood一个属性状态,那么在修改的时候属性是合并还是替换呢??
答:
state中进行修改状态的时候,其实是一种合并属性,而并不是替换属性;
说白了就是替换同名的属性,不同名的仍然保留。
问题2:类中的构造器被调用了几次??
答:
当new一个类的时候,就会产生一个实例对象,当在new的过程中代码中出现了构造器就会执行,
所以构造器执行了几次,需要看页面中有几个类的组件实例,有几个组件实例,那么构造器就会执行几次。
问题3:类中的render被调用了几次??
答:1 + n次
1次是初始化时候的次数,n是状态更新的次数
问题4:类中的changeMood被调用了几次??
答:事件发生几次,这个函数就会执行几次
问题5:类中添加构造器的作用是什么??
答:需要借助构造器进行初始化状态,还可以解决this指向问题
2.2 props
- 介绍
props
是 『properties』 的缩写, 也是类式组件实例对象的属性
- 作用
如果想要在组件的外部向组件的内部传数据,那么就可以利用props
-
语法: <组件 属性名=“属性值”/>
在类组件中使用this.props.data就可以取得属性名中的值
在函数组件中使用props.data就可以取得属性名中的值注意:其中属性名中可以任意指定但是组件中的和获取props要对应好了
-
批量传递props 使用
...扩展运算符
语法:<组件 {...obj}/>
2.2.1 props中的children属性
props.children可以获取组件标签中间的内容
//单标签
<Student {...item} } children = 值/>
//双标签
<Student>值 通过props.children获取<Student/>
2.2.2 props 的特点
-
可以给组件传递任意类型的数据
-
props 是只读的对象,只能读取属性的值,不要修改props
-
可以通过
...
运算符来将对象的多个属性分别传入子组件 -
如果父组件传入的是动态的 state 数据, 那一旦父组件更新 state 数据, 子组件也会更新
2.3 refs
refs 提供了一种允许我们访问 DOM 节点或在 render 方法中创建的 React 元素的写法。
ref是虚拟DOM对象的一个属性。
作用:方便在组件中获取真实DOM对象
refs的语法场景有三种:字符串形式、回调函数形式、createRef形式
- 字符串形式的ref【最简单,但是不推荐】
语法:<标签 ref=“名称”>
通过this.refs对象来获取值
- 回调函数形式的ref【官方推荐】
语法:
<标签 ref={currentNode => this.名称 = currentNode}>
获取:this.名称
回调函数中接受一个参数,参数名可以自定义,表示的是当前的节点对象。
ref的回调函数写法:ref={形式参数名=>this.属性名 = 形式参数名}
其中形式参数名在函数身上可以自定义,表达当前所在的虚拟DOM元素对象
this后面的属性名则直接添加在类组件实例化对象身上
ref属性只有值为字符串的形式的时候,才会需要使用this.refs的写法
除此之外后两种ref的用法都会直接添加在类的组件实例化对象身上
思考:ref的回调函数具体执行了几次??
摘自官方文档:
如果
ref
回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数null
,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,
所以 React 清空旧的 ref 并且设置新的。
通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,
但是大多数情况下它是无关紧要的。
- createRef的使用【
官方最推荐的
】
语法:
React.createRef()
<script type="text/babel">
class Demo extends React.Component{
/**
* React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是"专人专用"的
* **/
myRef = React.createRef();// {current:null}
myRef2 = React.createRef();
render(){
return (
<div>
<input ref={this.myRef} type="text"/>
<button onClick={this.ClickFun}>提示输入数据</button>
<input ref={this.myRef2} onBlur={this.BlurFun} type="text"/>
</div>
)
}
ClickFun = ()=>{
alert(this.myRef.current.value);
}
BlurFun = ()=>{
alert(this.myRef2.current.value);
}
}
//渲染组件到页面
ReactDOM.render(<Demo/>,document.getElementById("app"))
</script>
三、React中的事件处理
虽然之前咱们可以使用ref来标识想要的节点对象,但是官网提示【勿过度使用 Refs】,可以通过event.target得到发生事件的DOM元素对象,一般用于当发生事件的元素恰好是你要操作的元素就可以避免使用ref。
总结:
通过onXxx属性指定事件处理函数(注意大小写)
React使用的是自定义(合成)事件, 而不是使用的原生DOM事件
React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)
通过
event.target得到发生事件的DOM元素对象
react
一、React组件
组件允许你将 UI 拆分为独立可复用的代码片段,包括JS/CSS/HTML等。
组件从概念上类似于 JavaScript 函数。
它接收参数(即 “props”),内部可以有自己的数据(即 “state”),并返回用于描述页面展示的 React 元素。
一个 React 应用就是由一个个的 React 组件组成的
1.1 函数组件
-
定义
要求组件名称的首字母需要大写
function 组件名称(){ // 返回一段jsx的视图结构 return <div></div> }
-
调用
-
<组件名称></组件名称>
或
-
<组件名称 />
-
<body>
<!--1、创建容器元素-->
<div id="root"></div>
<!--2、引入相关的js文件-->
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<script src="./js/babel.min.js"></script>
<!--3、实现js代码-->
<script type="text/babel">
//3.1 声明一个函数
//注意:首字母必须大写!!
function Header(){
return <h3>这是一个h3元素</h3>
}
//3.2 渲染该函数组件
//在 React 内部会调用 Header 函数, 得到虚拟 DOM 对象,
//最终将虚拟 DOM 对象转化为真实 DOM, 渲染到容器中
ReactDOM.render(<Header/>,document.querySelector("#root"));
</script>
</body>
当如果有多个组件需要同时渲染,一定要注意,结构中必须包含根标签!!!
<script type="text/babel">
//3.1 声明一个函数
//注意:首字母必须大写!!
function Header(){
return <h3>这是一个h3元素</h3>
}
function Nav(){
return <ul>
<li>首页</li>
<li>列表页</li>
</ul>
}
//3.2 渲染该函数组件
//渲染单组件
// ReactDOM.render(<Header/>,document.querySelector("#root"));
//渲染多组件
ReactDOM.render(<div>
<Header></Header>
<Nav></Nav>
</div>,document.querySelector("#root"))
</script>
- 组件名首字母必须大写. 因为react以此来区分组件元素/标签 和 一般元素/标签
- 组件内部如果有多个标签,必须使用一个根标签包裹.只能有一个根标签
- 必须有返回值.返回的内容就是组件呈现的结构, 如果返回值为 null,表示不渲染任何内容
- 会在组件标签渲染时调用, 但不会产生实例对象, 没有状态
- 后面我们会讲如何在函数组件中定义状态 ==> hook语法
1.2 类组件
- 定义
class 类组件名 extends React.Component{
render(){
return <div></div>
}
}
-
解释
- extends:继承
- React.Component: react提供的一个类组件的基础类(父类)
-
注意
- render必须是一个方法
- return的必须是组件类的视图结构
-
特点
- 类组件有自己的生命周期和状态数据
-
调用
-
<组件名称></组件名称>
或
-
<组件名称 />
-
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!--1、引入react核心文件-->
<script src="./js/react.development.js"></script>
<!--2、引入react操作DOM文件-->
<script src="./js/react-dom.development.js"></script>
<!--3、babel进行转换jsx语法-->
<script src="./js/babel.min.js"></script>
<style>
header {
width: 100%;
height: 100px;
background: #456;
}
.main {
width: 100%;
height: 100px;
background: #569;
}
</style>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
class Header extends React.Component {
//在类中不光有constructor,还有render也是自动调用
//先调用constructor,主要在类式组件实例对象添加一些初始化的属性
//在调用render方法,将return后面返回的虚拟DOM元素对象进行返回
constructor() {
super();
console.log(this); //类式组件的实例化对象
//在实例化对象身上添加初始化属性
this.a = 1;
console.log('调用构造器了....')
}
render() {
console.log('调用render方法了...');
console.log(this); //类式组件的实例化对象
return <header>头部</header>
}
}
class Main extends React.Component {
render() {
return <main className="main">中间</main>
}
}
//创建一个类式组件
class App extends React.Component {
render() {
return <div>
<Header></Header>
<Main></Main>
</div>
}
}
//将组件中的虚拟DOM元素对象转化成真实的DOM元素对象,并且挂载到root元素
//只要一个类式组件调用的时候,都会默认执行实例化,进而得到一个类组件的实例化对象
ReactDOM.render(<App />, document.querySelector('#root'))
</script>
</body>
* 使用类式组件的注意事项:
* 1、组件名首字母大写
* 2、每一个类式组件的继承父类都是React.Component
* 3、类式组件中必须要有render方法,通过render方法来进行返回虚拟DOM元素对象
* 4、类式组件调用时相当于ES6中的new 类()的写法,产生组件实例化对象,
* 在组件实例化对象中可以使用像state、props、ref等属性
* 5、在return返回的虚拟DOM元素对象中必须要有一个绝对唯一的根标签,
如果在return后面换行书写,需要添加一个()
* 6、想要给组件实例化对象身上添加初始化的属性,则可以使用constructor构造器,
如果没有明确写出,则系统会默认分配一个空的构造器
如果明确写出构造器,则在构造器中的第一句话需要调用super()方法
* 7、如果类中既有constructor,又有render方法,
则先执行constructor,在执行render,且这两个方法都是自动执行
* 8、其中的this均指向类组件的实例化对象
二、React组件实例的三大核心属性
2.1 state
React 把组件看成是一个状态机(State Machines)。
通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。
React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面。
值是一个对象(可以包含多个key-value)
基本使用
-
初始化 state
- 构造器中: this.state = {xxx: 2}
- 类体中: state = {xxx: 2}
-
读取state数据
- this.state.xxx
-
更新state数据
- 不能直接更新state数据
- 必须
this.setState({ 要修改的属性数据 })
异步方法
<body>
<!--1.准备一个容器 -->
<div id="app"></div>
<!--2.引入js文件-->
<!--2.1 引入react核心库文件-->
<script src="js/react.development.js"></script>
<!--2.2 引入react-dom 用于支持react操作DOM-->
<script src="js/react-dom.development.js"></script>
<!--2.3 引入babel 用于将jsx转为js-->
<script src="js/babel.min.js"></script>
<script type="text/babel">
//1.创建类式组件
class Mood extends React.Component{
constructor() {
super();
//初始化状态
this.state = {
isGood:false //在这个对象中可以根据实际开发需求存入多组key-value
}
}
render(){
//使用this.state.isGood这种方式相对较繁琐
// return <h1>今天心情很{this.state.isGood ? '好' : '不好'}</h1>
//也可以使用对象的解构赋值语法.
const {isGood} = this.state.isGood;
return <h1>今天心情很{isGood ? '好' : '不好'}</h1>
}
}
//2.渲染组件到页面
ReactDOM.render(<Mood/>,document.getElementById("app"))
</script>
</body>
注意:
React中的state是React实例对象中的其中一个很重要的属性
当如果想要对一个类的实例对象进行一些初始化的操作
(增加一个属性 / 修改一个属性的值等)都需要借助class类中的构造器constructor函数来完成。
React官网中要求state的值必须为一个{}对象
class类组件中的render方法中的this表示当前组件实例对象
- state中的事件绑定
<body>
<!--1.准备一个容器 -->
<div id="app"></div>
<!--2.引入js文件-->
<!--2.1 引入react核心库文件-->
<script src="js/react.development.js"></script>
<!--2.2 引入react-dom 用于支持react操作DOM-->
<script src="js/react-dom.development.js"></script>
<!--2.3 引入babel 用于将jsx转为js-->
<script src="js/babel.min.js"></script>
<script type="text/babel">
//1.创建类式组件
class Mood extends React.Component{
constructor() {
super();
this.state = {
isGood:false
}
}
//3.声明需要点击的事件函数
demo(){
console.log('标题被点击了~~~');
}
render(){
//使用this.state.isGood这种方式相对较繁琐
// return <h1>今天心情很{this.state.isGood ? '好' : '不好'}</h1>
//也可以使用对象的解构赋值语法.
const {isGood} = this.state.isGood;
return <h1 onClick={this.demo}>今天心情很{isGood ? '好' : '不好'}</h1>
}
}
//2.渲染组件到页面
ReactDOM.render(<Mood/>,document.getElementById("app"))
</script>
</body>
注意:
在虚拟DOM元素身上绑定事件的时候,事件名遵循小驼峰写法。
事件函数直接使用{函数名}的写法,不能像原生JS中写入一个字符串,
而且也不要在{}中直接调用函数,这样会脱离事件,页面加载后直接运行函数内容。
- 类中方法的this
前面咱们已经学到了如何在虚拟DOM元素身上绑定事件,虽然实现了事件绑定,但是之前想要做的事情其实是需
要通过这个点击的事件函数来修改state中的状态属性值,所以就需要来具体研究类中方法的this。
问题: 类中定义的事件回调方法中
this
是undefined
, 无法更新state原因:
事件回调都不是组件对象调用的, 都是事件触发后,直接调用的,
class中所有方法都使用严格模式, 所以方法中的this就是undefined
解决办法1 - 包裹箭头函数
原因: render中的this是组件对象, 处理函数是我们通过组件对象来调用的解决办法2 - bind绑定this
原因: 构造器中的this是组件对象, 将处理函数通过bind绑定为了组件对象解决办法3 - 箭头函数
原理: 利用bind给事件回调绑定this为组件对象(render中的this)
选择:
- 一般用
箭头函数
方式, 编码简洁- 如果要传递特定的数据, 选择用
包裹箭头函数
方式
<body>
<!--1.准备一个容器 -->
<div id="app"></div>
<!--2.引入js文件-->
<!--2.1 引入react核心库文件-->
<script src="js/react.development.js"></script>
<!--2.2 引入react-dom 用于支持react操作DOM-->
<script src="js/react-dom.development.js"></script>
<!--2.3 引入babel 用于将jsx转为js-->
<script src="js/babel.min.js"></script>
<script type="text/babel">
//1.创建类式组件
class Mood extends React.Component{
constructor() {
super();
this.state = {
isGood:false
}
}
render(){
const {isGood} = this.state.isGood;
return <h1 onClick={this.changeMood}>今天心情很{isGood ? '好' : '不好'}</h1>
}
//3.声明需要点击的事件函数(类中定义方法,不能写function)
/*
* 1. changeMood具体是声明在哪里的?? --- Mood的原型对象身上,目的让实例对象使用
* 2. 由于changeMood是作为onClick的回调函数,不是直接通过实例对象来调用,而是直接调用
* 3. 类中方法默认开启了局部的严格模式,所以changeMood中的this为undefined
*/
changeMood(){
console.log('标题被点击了~~~');
}
}
//2.渲染组件到页面
ReactDOM.render(<Mood/>,document.getElementById("app");
</script>
</body>
报错:Cannot read property ‘state’ of undefined
原因:默认情况下demo函数中的this指向了window对象,但由于babel在解析代码时开启了严格模式,
禁止自定义的函数里面的this指向window对象,所以为undefined
注意:
- 在类中声明的方法都在类的原型对象上。
- 在类中声明的方法在函数内部都默认自动开启了严格模式,
回忆class中的方法中的this指向问题
//复习class类中方法的this相关问题
class Person{
constructor(name,age){
this.name = name;
this.age = age;
}
//在类中声明的方法都在类的原型对象上
study(){
console.log('学习');
console.log(this);
}
}
const p1 = new Person('张三',23);
p1.study();
在类中声明的方法在函数内部都默认自动开启了严格模式,
<script>
//复习原生JS中的函数严格模式下的this
function demo(){
//在函数内部开启严格模式
'use strict';
console.log(this); //undefined
}
//调用函数
demo();
</script>
具体如何解决this指向的问题呢?
其实很简单,既然this指向丢失了,那么我们修改成想要的this指向不就行了么?
- 方式一:箭头函数
<script type="text/babel">
//1.创建类式组件
class Mood extends React.Component{
constructor() {
super();
this.state = {
isGood:false
}
}
render(){
const {isGood} = this.state;
return <h1 onClick={this.changeMood}>今天心情很{isGood ? '好' : '不好'}</h1>
}
//3.声明需要点击的事件函数
changeMood=()=>{
console.log(this.state.isGood);
}
}
//2.渲染组件到页面
ReactDOM.render(<Mood/>,document.getElementById("app");
</script>
- 方式二:bind修改this指向
<script type="text/babel">
//1.创建类式组件
class Mood extends React.Component{
constructor() {
super();
this.state = {
isGood:false
}
//在初始化Mood的实例对象的时候,将changeMood中的this进行修改,但是不调用方法
//由于实例对象身上没有changeMood这个方法,但是会顺着原型链向下查找
//左侧的this.changeMood是在实例对象身上添加一个叫changeMood的方法
//右侧的this.changeMood是利用实例对象中向Mood的原型对象身上查找changeMood这个方法
this.changeMood = this.changeMood.bind(this);
}
render(){
const {isGood} = this.state;
return <h1 onClick={this.changeMood}>今天心情很{isGood ? '好' : '不好'}</h1>
}
//3.声明需要点击的事件函数
changeMood(){
console.log('标题被点击了~~~');
}
}
//2.渲染组件到页面
ReactDOM.render(<Mood/>,document.getElementById("app");
</script>
- 方式三:包裹箭头函数
<script type="text/babel">
//1.创建类式组件
class Mood extends React.Component{
constructor() {
super();
this.state = {
isGood:false
}
}
render(){
const {isGood} = this.state;
return <h1 onClick={()=>{this.changeMood()}}>
今天心情很{isGood ? '好' : '不好'}
</h1>
}
//3.声明需要点击的事件函数
changeMood(){
console.log(this.state.isGood);
}
}
//2.渲染组件到页面
ReactDOM.render(<Mood/>,document.getElementById("app");
</script>
- setState
特别注意:state状态数据不能直接修改或者是更新!!!!,且setState是异步更新
<script type="text/babel">
//1.创建类式组件
class Mood extends React.Component {
constructor() {
super();
this.state = { isGood: false }
this.changeMood = this.changeMood.bind(this);
}
render() {
const { isGood } = this.state;
return <h1 onClick={this.changeMood}>今天心情很{isGood ? '好' : '不好'}</h1>
}
//3.声明需要点击的事件函数
changeMood() {
//4. 修改state中的isGood数据
//const isGood = this.state.isGood;
//this.setState({isGood:!isGood})
//或者
this.setState(state => {
let isGood = !state.isGood
console.log(isGood)
//需要返回一个对象,对象的键名需要是状态中的属性名
return { isGood }
})
}
}
//2.渲染组件到页面
ReactDOM.render(<Mood />, document.getElementById("app"))
</script>
虽然前面的代码中咱们已经看到了isGood的状态值可以相互之间进行切换了,
但是还要有一些细节的问题思考一下,
问题1:如果state中的不光有isGood一个属性状态,那么在修改的时候属性是合并还是替换呢??
答:
state中进行修改状态的时候,其实是一种合并属性,而并不是替换属性;
说白了就是替换同名的属性,不同名的仍然保留。
问题2:类中的构造器被调用了几次??
答:
当new一个类的时候,就会产生一个实例对象,当在new的过程中代码中出现了构造器就会执行,
所以构造器执行了几次,需要看页面中有几个类的组件实例,有几个组件实例,那么构造器就会执行几次。
问题3:类中的render被调用了几次??
答:1 + n次
1次是初始化时候的次数,n是状态更新的次数
问题4:类中的changeMood被调用了几次??
答:事件发生几次,这个函数就会执行几次
问题5:类中添加构造器的作用是什么??
答:需要借助构造器进行初始化状态,还可以解决this指向问题
案例:点击按钮自增更新次数
<body>
<!--1.准备一个容器 -->
<div id="app"></div>
<!--2.引入js文件-->
<!--2.1 引入react核心库文件-->
<script src="js/react.development.js"></script>
<!--2.2 引入react-dom 用于支持react操作DOM-->
<script src="js/react-dom.development.js"></script>
<!--2.3 引入babel 用于将jsx转为js-->
<script src="js/babel.min.js"></script>
<script type="text/babel">
//1.创建类式组件
class Count extends React.Component{
constructor() {
super();
//初始化状态
this.state = {
count: 0 //在这个对象中可以根据实际开发需求存入多组key-value
}
}
render(){
const { count } = this.state;
return (
<div>
<span>计数:{count}</span><br />
<button onClick={() => {
this.setState({
count: count + 1
})
}}>点击增加次数</button>
</div>
)
}
}
//2.渲染组件到页面
ReactDOM.render(<Count/>,document.getElementById("app"))
</script>
</body>
- state的简写形式
<body>
<script>
class Person{
constructor(name,age){
this.name = name;
this.age = age;
// this.hobby = '敲代码'
}
//在类的所有实例对象身上添加属性
//虽然是在类中直接添加,但是如果实例中需要从外部传递数据的话,
//还是需要在构造器中完成,如果是一个固定的值就可以直接在类中定义
hobby = '敲代码';
}
const p1 = new Person('张三',23);
const p2 = new Person('李四',23);
console.log(p1);
console.log(p2);
</script>
</body>
前面虽然咱们考虑了细节,针对于代码也做了完善,但是仍然考虑不周到,
例如:当事件函数出现了多个时,this全部丢失,难道每一个函数都要在构造函数中修改指向??
<script type="text/babel">
//1.创建类式组件
class Mood extends React.Component {
//初始化状态
state = { isGood: false , weather:'阴天' }
render() {
const { isGood , weather } = this.state;
return <h1 onClick={this.changeMood}>今天天气是{weather},所以心情很{isGood ? '好' : '不好'}</h1>
}
//3.声明需要点击的事件函数
changeMood = ()=>{
//4. 修改state中的isGood数据
//箭头函数本身是没有自己的this指向,需要看外侧函数的指向
const isGood = this.state.isGood;
this.setState({isGood:!isGood})
}
}
//2.渲染组件到页面
ReactDOM.render(<Mood />, document.getElementById("app"))
</script>
总结:
组件中的render方法中的this为组件实例对象
组件自定义方法中的this为undefined,如何解决?
- 强制绑定this:通过函数对象中的bind
- 箭头函数
状态数据,不能直接修改或更新
组件自定义方法在类的原型上无法直接定义为一个箭头函数,必须使用赋值成一个箭头函数
2.2 props
- 介绍
props
是 『properties』 的缩写, 也是类式组件实例对象的属性
- 作用
如果想要在组件的外部向组件的内部传数据,那么就可以利用props
- 语法: <组件 属性名=“属性值”/>
在组件中使用this.props.data就可以取得属性名中的值。
在函数组件中使用props.data就可以取得属性名中的值
注意:其中属性名中可以任意指定但是组件中的和获取props要对应好了
- 基本使用
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="js/react.development.js"></script>
<script src="js/react-dom.development.js"></script>
<script src="js/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
//父组件
class App extends React.Component {
render() {
let obj = {
name: '张三',
age: 23,
sex: '男'
}
return (
<div>
<Student name={obj.name} age={obj.age} sex={obj.sex} />
</div>
)
}
}
//子组件
class Student extends React.Component {
render() {
const { name, age, sex } = this.props;
return (
<div>
<ul>
<li>姓名:{name}</li>
<li>年龄:{age}</li>
<li>性别:{sex}</li>
</ul>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'));
</script>
</body>
- 批量传递props
前面虽然已经GET到了props的好用之处,但是有很多实际的问题隐含在里面。
例如:
a. 当组件实例中存在多个属性,全部罗列在组件上,代码一定会显得非常长,不太好
b. 组件实例中的一些属性数据,大多数不会自己随意定义,而是从服务器中获取出来的
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="js/react.development.js"></script>
<script src="js/react-dom.development.js"></script>
<script src="js/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
//父组件
class App extends React.Component {
render() {
let stus = [
{
sname: '张三丰',
sage: 98,
ssex: '男',
shobby: ['打太极拳', '耍太极剑'],
sheight: 180
},
{
sname: '张无忌',
sage: 78,
ssex: '男',
shobby: ['打太极拳', '耍太极剑'],
sheight: 170
},
{
sname: '周芷若',
sage: 20,
ssex: '女',
shobby: ['九阴白骨爪'],
sheight: 170
}
]
return (
<div>
{
stus.map((item, index) => {
return <Student {...item} key={index} />
})
}
</div>
)
}
}
//子组件
class Student extends React.Component {
render() {
const { sname, sage, ssex, shobby, sheight } = this.props;
return (
<div>
<ul>
<li>姓名:{sname}</li>
<li>年龄:{sage}</li>
<li>性别:{ssex}</li>
<li>技能:{shobby.join(',')}</li>
<li>身高:{sheight}</li>
</ul>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'));
</script>
</body>
2.2.1 props中的children属性
props.children可以获取组件标签中间的内容
//单标签
<Student {...item} } children = 值/>
//双标签
<Student>值 通过props.children获取<Student/>
2.2.2 props 的特点
- 可以给组件传递任意类型的数据
- props 是只读的对象,只能读取属性的值,不要修改props
let { name, age, hobby, fn, isMan } = this.props;
let obj = { a: 1, b: 2 }
this.props = obj;
console.log(this.props); //还是原来的对象值,且还会报错
- 可以通过
...
运算符来将对象的多个属性分别传入子组件 - 如果父组件传入的是动态的 state 数据, 那一旦父组件更新 state 数据, 子组件也会更新
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="js/react.development.js"></script>
<script src="js/react-dom.development.js"></script>
<script src="js/babel.min.js"></script>
<style>
.btn {
display: inline-block;
margin-bottom: 0;
font-weight: 400;
text-align: center;
white-space: nowrap;
vertical-align: middle;
-ms-touch-action: manipulation;
touch-action: manipulation;
cursor: pointer;
background-image: none;
border: 1px solid transparent;
padding: 6px 12px;
font-size: 14px;
line-height: 1.42857143;
border-radius: 4px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
margin-right: 20px;
}
.btn-primary {
color: #fff;
background-color: #337ab7;
border-color: #2e6da4;
}
</style>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
//父组件
class App extends React.Component {
state = {
isMan: true
}
render() {
const { isMan } = this.state;
return <div>
<Button
name={"张三"}
age={23}
isMan={isMan}
hobby={['吃饭', '睡觉', '打游戏']}
/>
<button className="btn btn-primary" onClick={() => {
this.setState({
isMan: !this.state.isMan
})
}}>修改性别的值</button>
</div>
}
}
//子组件
class Button extends React.Component {
render() {
let { name, age, hobby, fn, isMan } = this.props;
return (
<div>
<p>姓名:{name}</p>
<p>年龄:{age}</p>
{isMan ? <p>性别:男</p> : <p>性别:女</p>}
<p>爱好:{hobby.join('/')}</p>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'));
</script>
</body>
- 函数式组件的props
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="js/react.development.js"></script>
<script src="js/react-dom.development.js"></script>
<script src="js/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
//父组件
class App extends React.Component {
render() {
//创建一个对象
let info = {
name: '张三',
age: 23,
hobby: ['吃饭', '睡觉', '打豆豆']
}
return (
<div>
<Stu {...info} />
</div>
)
}
}
//子组件
function Stu(props) {
let { name, age, hobby } = props
return (
<div>
<p>姓名:{name}</p>
<p>年龄:{age}</p>
<p>爱好:{hobby.join('-')}</p>
</div>
)
}
ReactDOM.render(<App />, document.getElementById('root'));
</script>
</body>
2.3 refs
refs 提供了一种允许我们访问 DOM 节点或在 render 方法中创建的 React 元素的写法。
ref是虚拟DOM对象的一个属性。
作用:方便在组件中获取真实DOM对象
refs的语法场景有三种:字符串形式、回调函数形式、createRef形式
- 字符串形式的ref【最简单,但是不推荐】
语法:<标签 ref=“名称”>
获取:通过this.refs对象来获取值
先来实现一个案例:
需求: 自定义组件, 功能说明如下:
1. 点击按钮, 提示第一个输入框中的值
2. 当第2个输入框失去焦点时, 提示这个输入框中的值
<script type="text/babel">
class Demo extends React.Component{
render(){
return (
<div>
<input ref="ipt1" type="text"/>
<button onClick={this.ClickFun}>提示输入数据</button>
<input onBlur={this.BlurFun} ref="ipt2" type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
ClickFun = ()=>{
const {ipt1} = this.refs;
alert(ipt1.value);
}
BlurFun = ()=>{
const {ipt2} = this.refs;
alert(ipt2.value);
}
}
//渲染组件到页面
ReactDOM.render(<Demo/>,document.getElementById("app"))
</script>
- 回调函数形式的ref【官方推荐】
语法:
<标签 ref={currentNode => this.名称 = currentNode}>
获取:this.名称
回调函数中接受一个参数,参数名可以自定义,表示的是当前的节点对象。
<script type="text/babel">
class Demo extends React.Component{
render(){
return (
<div>
{/* 在组件实例对象身上添加一个属性名(ipt),属性值为当前input节点对象 */}
<input ref={(currentNode)=>{this.ipt = currentNode}} type="text"/>
<button onClick={this.ClickFun}>提示输入数据</button>
<input onBlur={this.BlurFun} ref={(currentNode)=>{this.ipt1 = currentNode}} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
ClickFun = ()=>{
const {ipt} = this;
alert(ipt.value);
}
BlurFun = ()=>{
const {ipt1} = this;
alert(ipt1.value);
}
}
//渲染组件到页面
ReactDOM.render(<Demo/>,document.getElementById("app"))
</script>
思考:ref的回调函数具体执行了几次??
摘自官方文档:
如果
ref
回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数null
,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,
所以 React 清空旧的 ref 并且设置新的。
通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,
但是大多数情况下它是无关紧要的。
- createRef的使用【
官方最推荐的
】
语法:React.createRef()
<script type="text/babel">
class Demo extends React.Component{
/**
* React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是"专人专用"的
* **/
myRef = React.createRef();// {current:null}
myRef2 = React.createRef();
render(){
return (
<div>
<input ref={this.myRef} type="text"/>
<button onClick={this.ClickFun}>提示输入数据</button>
<input ref={this.myRef2} onBlur={this.BlurFun} type="text"/>
</div>
)
}
ClickFun = ()=>{
alert(this.myRef.current.value);
}
BlurFun = ()=>{
alert(this.myRef2.current.value);
}
}
//渲染组件到页面
ReactDOM.render(<Demo/>,document.getElementById("app"))
</script>
案例:
三、React中的事件处理
虽然之前咱们可以使用ref来标识想要的节点对象,但是官网提示【勿过度使用 Refs】,可以通过event.target得到发生事件的DOM元素对象,一般用于当发生事件的元素恰好是你要操作的元素就可以避免使用ref。
class Demo extends React.Component{
myRef = React.createRef();
render(){
return (
<div>
<input ref={this.myRef} type="text"/>
<button onClick={this.ClickFun}>提示输入数据</button>
<input onBlur={this.BlurFun} type="text"/>
</div>
)
}
ClickFun = ()=>{
alert(this.myRef.current.value);
}
BlurFun = (event)=>{
console.log(event.target.value);
}
}
//渲染组件到页面
ReactDOM.render(<Demo/>,document.getElementById("app"))
总结:
通过onXxx属性指定事件处理函数(注意大小写)
React使用的是自定义(合成)事件, 而不是使用的原生DOM事件
React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)
通过event.target得到发生事件的DOM元素对象