前言
- 先简单记录下,后续慢慢在慢慢补充吧;
React创建组件的两种方式
Class
:- 已被官方废弃(源码中依然保留),但是需要了解学习,为函数式组件做铺垫;
- 可以直接反映出React运行时的整个生命周期;
- 很多的项目中还是有用Class组件的;
Function
(纯函数):- 后续都会使用这种方式;
类组件样板代码
TS
ts
import React from 'react';
interface PropsType {};
interface StateType {};
class ClassCom extends React.Component<PropsType, StateType> {
/** 会在组件挂载前执行 */
// class组件的执行过程,首先肯定是执行实例化,也就是执行 constructor;
constructor(props: PropsType) {
super(props);
// 在这里定义状态
this.state = {};
}
handleClick() {
console.log('嘻嘻哈哈');
}
// 渲染组件:【constructor之后、挂载之前执行】
render() {
return (
<div className="class-com">
ClassComponent
<button onClick={() => this.handleClick()}>click</button>
</div>
)
}
}
export default ClassCom;
JS
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
这个第三方模块指定类型; - 如果传递的属性类型不匹配,控制台就会有警告;
js
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>
);
}
};
// 给组件添加 propTypes【自定义】 属性
Son.propTypes = {
// 指定 name 为 字符串,并且 必传
name: PropTypes.string.isRequired,
age: PropTypes.number.isRequired,
// 指定 like 为 数组,非必传
like: PropTypes.array
};
tsconstructor(props) { super(props); }
- 类函数
constructor
基础代码解释:- 这段代码是JS类构造函数的一部分,功能如下:
- 调用父类的构造函数
super(props)
,将传入的props
参数传递给父类构造函数进行初始化- 确保子类能够正确继承和初始化父类的状态和属性;
一、状态的定义和修改
-
目标位置:
constructor()
;
-
定义状态 :
- 这是类组件 ,定义
state
的时候要在this.state
中去定义;
- 这是类组件 ,定义
-
访问状态 :
- 访问的时候,也要基于
this.state
去访问(可以进行解构);
- 访问的时候,也要基于
-
修改状态 :
- 修改
state
的时候,要使用this.setState()
进行修改;
- 修改
-
示例:
tsimport React, { ReactNode } from 'react'; interface PropsType {}; interface StateType { name: string; }; class ClassCom extends React.Component<PropsType, StateType> { constructor(props: PropsType) { super(props); // 定义状态 - this.state this.state = { name: 'React 类组件 ClassComponent' }; } handleClick = () => { // 修改state - this.setState this.setState({ name: new Date().getTime(), }); }; render() { // 访问state - 解构 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
正确绑定。
- 在 JSX 中使用箭头函数来调用方法,这样可以确保
- ❌ 在构造函数中绑定
js
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
this.editNewLike = this.editNewLike.bind(this);
}
// 方式二 - 箭头函数
editNewAge = () => {
this.setState({ newAge: 30 });
};
// 方式三 - 在JSX中绑定this
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>
);
}
};
// 给自组件添加 propTypes 属性
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做出修改的时候,页面上的只是不会更新的,都被拦截掉了(覆盖了);
- 返回的指会对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
进行比较来确定发生了什么变化;
- 渲染之前的值;
- 返回值 :
- 应该返回想要的任何类型的快照值或者是
null
;
- 应该返回想要的任何类型的快照值或者是
- 使用场景 :
- 有个长列表,需要不断的往里面加入内容;
- 更新之前,记住滚轮的位置,传递给
componentDidUpdate
,保持滚轮的位置(提升用户体验);
- 更新之前,记住滚轮的位置,传递给
- 有个长列表,需要不断的往里面加入内容;
- 执行时机 :
- 最后执行
componentDidUpdate(prevProps, prevState, snapshot?)
;- 执行时机 ;
- React会在组件更新了props和state 重新渲染后 立即调用它;
- 参数 :
prevProps
:- 更新之前的props;
prevProps
将会与this.props
进行比较来确定发生了什么变化;
prevState
:- 更新之前的state;
prevState
将会与this.state
进行比较来确定发生了什么变化;
snapshot
(非必传):- 如果你实现了
getSnapshotBeforeUpdate
,那么snapshot将包含从该方法返回的值。否则它就是undefined;
- 如果你实现了
- 返回值 :
- 不应该返回任何值;
- 使用场景 :
- 可以在一次更新后使用它来操作DOM;
- 注意 :
- 如果你定义了
shouldComponentUpdate
并且返回值为false
的话,那么componentDidUpdate
将不会被调用; - 建议不要在该钩子函数中直接调用
setState
,会造成性能问题;
- 如果你定义了
- 执行时机 ;
2.3 卸载时
- 组件卸载时,会将状态进行重置;
componentWillUnmount()
;- 如果你定义了
componentWillUnmount
方法,React 会在你的组件 卸载 之前调用它。 - 此方法常常用于取消数据获取或移除监听事件。
- 该钩子函数没有任何参数也没有任何的返回值;