前言
React创建组件的两种方式
Class
:
- 已被官方废弃(源码中依然保留),但是需要了解学习,为函数式组件做铺垫;
- 可以直接反映出React运行时的整个生命周期;
- 很多的项目中还是有用Class组件的;
Function
(纯函数):
类组件样板代码
TS
import React from 'react';
interface PropsType {};
interface StateType {};
class ClassCom extends React.Component<PropsType, StateType> {
constructor(props: PropsType) {
super(props);
this.state = {};
}
handleClick() {
console.log('嘻嘻哈哈');
}
render() {
return (
<div className="class-com">
ClassComponent
<button onClick={() => this.handleClick()}>click</button>
</div>
)
}
}
export default ClassCom;
JS
import { Component } from 'react';
export default class ClassCom extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return (
<div>
<h1>我是ClassCom组件</h1>
</div>
);
}
}
JS指定props类型
- 对于TS,我们可以指定
props
中每个属性的类型,减少后期开发的类型错误;
- 而对于JS,我们可以
prop-types
这个第三方模块指定类型;
- 如果传递的属性类型不匹配,控制台就会有警告;
import { Component } from 'react';
import PropTypes from 'prop-types';
export default class Son extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return (
<div>
<h1>我是子组件</h1>
</div>
);
}
};
Son.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number.isRequired,
like: PropTypes.array
};
constructor(props) {
super(props);
}
- 类函数
constructor
基础代码解释:
- 这段代码是JS类构造函数的一部分,功能如下:
- 调用父类的构造函数
super(props)
,将传入的 props
参数传递给父类构造函数进行初始化
- 确保子类能够正确继承和初始化父类的状态和属性;
一、状态的定义和修改
- 目标位置:
- 定义状态:
- 这是类组件,定义
state
的时候要在this.state
中去定义;
- 访问状态:
- 访问的时候,也要基于
this.state
去访问(可以进行解构);
- 修改状态:
- 修改
state
的时候,要使用this.setState()
进行修改;
- 示例:
import React, { ReactNode } from 'react';
interface PropsType {};
interface StateType {
name: string;
};
class ClassCom extends React.Component<PropsType, StateType> {
constructor(props: PropsType) {
super(props);
this.state = {
name: 'React 类组件 ClassComponent'
};
}
handleClick = () => {
this.setState({
name: new Date().getTime(),
});
};
render() {
const { name } = this.state;
return (
<>
<div className="class-com">{name}div>
<button onClick={this.handleClick}>edit name</button>
</>
)
}
}
二、绑定事件
- 在 React 类组件中,
this
关键字在类方法中默认是 undefined
,这是因为 JavaScript 中的类方法不会自动绑定 this
到类的实例。为了解决这个问题,可以使用以下几种方法之一:
- ❌ 在构造函数中绑定
this
:
- 在类的构造函数中使用
.bind(this)
来绑定 this
到类的实例。
- 使用箭头函数:
- 在定义方法时使用箭头函数,箭头函数会自动绑定
this
到定义时的上下文。
- 在 JSX 中绑定
this
:
- 在 JSX 中使用箭头函数来调用方法,这样可以确保
this
正确绑定。
import { Component } from 'react'
import PropTypes from 'prop-types';
export default class Son extends Component {
constructor(props) {
super(props);
this.state = {
newName: '嘻哈少将',
newAge: 20,
newLike: ['吃烧烤', '喝啤酒']
};
this.editNewLike = this.editNewLike.bind(this);
}
editNewAge = () => {
this.setState({ newAge: 30 });
};
editNewName() {
this.setState({ newName: '哈哈哈' });
};
editNewLike() {
this.setState({ newLike: ['唱', '跳', 'rap', '篮球'] });
};
render() {
const { name, age, like, } = this.props;
const { newName, newAge, newLike } = this.state;
return (
<div>
<h1>我是子组件</h1>
<div>old:{name} ===》 new:{newName}</div>
<div>old:{age} ===》 new:{newAge}</div>
{like && <div>old:{like.join('、')} ===》 new:{newLike.join('、')}</div>}
<button onClick={() => this.editNewName()}>edit new-name</button> <br />
<button onClick={this.editNewAge}>edit new-age</button> <br />
<button onClick={this.editNewLike}>edit new-like</button> <br />
</div>
);
}
};
Son.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number.isRequired,
like: PropTypes.array,
};
三、生命周期
生命周期示例图:
2.1 初始化(挂载时、创建时)
React Class
组件的执行过程,肯定先是执行类的实例化,也就是constructor
;
- 不管哪个生命周期,肯定都是在
constructor
之后执行的;
getDerivedStateFromProps
,在组件挂载之前执行(render
执行之前调用),返回值会赋给state
;
- 然后执行
render
函数,去渲染页面(挂载前);
- 渲染完成之后执行
componentDidMount
(挂载后);
2.2 更新时
- 当我们的状态更新时;
- 首先会触发
getDerivedStateFromProps(props, state)
生命周期钩子函数;
- 注意:
- 该方法是静态方法,定义的时候,需要在函数名前面添加
static
;
- 执行时机:
React
会在 初始挂载 和 后续更新时 调用render
之前调用它;
- 参数;
props
:组件即将用来渲染的下一个props;
state
:组件即将渲染的下一个state;
- 返回值:
- 返回一个对象来更新
state
;
- 返回的指会对state中的对应值进行覆盖;
- 相同属性覆盖;
- 返回的对象中有和state相同的属性就覆盖;
- 若没有相同的属性,就不会触发覆盖;
- 因为该钩子函数会在初始化挂载和更新时调用render之前调用,所以当对对应的state做出修改的时候,页面上的只是不会更新的,都被拦截掉了(覆盖了);
- 或者返回
null
就不更新任何内容;
- 注意:
- 该钩子函数的两个参数,若发生相同属性覆盖行为,就会发现,参数props和参数state与下次要渲染的props和state不相同;
- 当我们触发修改的时候,其实props和state都已经修改成功了,但是被该钩子函数返回的相同属性覆盖了;
- 接着会调用
getSnapshotBeforeUpdate(prevProps, prevState)
;
- 执行时机:
render
之后,页面真正更新之前;
- react只要执行了
setState
,render就会执行,也就是执行这些钩子函数;
- 作用:
- React会在React更新DOM之前直接调用它;
- 在更新之前获取一些信息;
- 参数:
prevProps
:
- 更新之前的Props;
prevProps
将会与this.props
进行比较来确定发生了什么变化;
prevState
:
- 更新之前的State;
prevState
将会与this.state
进行比较来确定发生了什么变化;
- 渲染之前的值;
- 返回值:
- 使用场景:
- 有个长列表,需要不断的往里面加入内容;
- 更新之前,记住滚轮的位置,传递给
componentDidUpdate
,保持滚轮的位置(提升用户体验);
- 最后执行
componentDidUpdate(prevProps, prevState, snapshot?)
;
- 执行时机;
- React会在组件更新了props和state 重新渲染后 立即调用它;
- 参数:
prevProps
:
- 更新之前的props;
prevProps
将会与this.props
进行比较来确定发生了什么变化;
prevState
:
- 更新之前的state;
prevState
将会与this.state
进行比较来确定发生了什么变化;
snapshot
(非必传):
- 如果你实现了
getSnapshotBeforeUpdate
,那么snapshot将包含从该方法返回的值。否则它就是undefined;
- 返回值:
- 使用场景:
- 注意:
- 如果你定义了
shouldComponentUpdate
并且返回值为false
的话,那么componentDidUpdate
将不会被调用;
- 建议不要在该钩子函数中直接调用
setState
,会造成性能问题;
2.3 卸载时
- 组件卸载时,会将状态进行重置;
componentWillUnmount()
;
- 如果你定义了
componentWillUnmount
方法,React 会在你的组件 卸载 之前调用它。
- 此方法常常用于取消数据获取或移除监听事件。
- 该钩子函数没有任何参数也没有任何的返回值;