1. 用了几年 Vue,但对于 React 还处在懵懂的状态,趁着最近工作不忙,赶紧把 React 的知识补上
  2. 强烈推荐哔哩哔哩上的张天禹老师的教程:尚硅谷React教程(2022加更,B站超火react教程)
  3. 有 vue 基础的上手特别快,有需要学习 React 的伙伴可以观看一下

什么是 React

React 是一个简单的 javascript UI 库,用于构建高效、快速的用户界面。它是一个轻量级库,因此很受欢迎。它遵循组件设计模式、声明式编程范式和函数式编程概念,以使前端应用程序更高效。它使用 虚拟DOM 来有效地操作 DOM。它遵循从高阶组件到低阶组件的单向数据流。

React 和 Vue 的区别

核心思想不同

  1. Vue 的核心思想是尽可能的降低前端开发的门槛,是一个灵活易用的渐进式双向绑定的 MVVM 框架。
  2. React 的核心思想是声明式渲染和组件化、单向数据流,React 既不属于 MVC 也不属于 MVVM 架构。

声明式是什么意思?

声明式与之相对应的是命令式,命令式指的是通过DOM操作一步步把网页变成想要的样子,而声明式则是只需要通过状态去形容最后的网页长什么样子即可。

组件化是什么意思?

组件化指的是尽可能的将页面拆分成一个个较小的、可以复用的组件,这样让我们的代码更加方便组织和管理,并且拓展性页更强。

如何理解React的单向数据流?

React 的单向数据流指的是数据主要从父节点通过 props 传递到子节点,如果顶层某个 props 改变了,React 会重新渲染所有的子节点,但是单向数据流并非单向绑定,React 想要从一个组件去更新另一个组件的状态,需要进行状态提升,即将状态提升到他们最近的祖先组件中,触发父组件的状态变更,从而影响另一个组件的显示。单向数据流的好处是能够保证状态改变的可追溯性,假如,父组件维护了一个状态,子组件如果能够随意更改父组件的状态,那么各组件的状态改变就会变得难以追溯。

组件写法上不同

  1. Vue 的组件写法是通过 template 的单文件组件格式。
  2. React 的组件写法是 JSX+inline style,也就是吧 HTML 和 CSS 全部写进 JavaScript 中。

diff 算法不同

  1. vue 对比节点,如果节点元素类型相同,但是 className 不同,认为是不同类型的元素,会进行删除重建,但是 react 则会认为是同类型的节点,只会修改节点属性。
  2. vue 的列表比对采用的是首尾指针法,而 react 采用的是从左到右依次比对的方式,当一个集合只是把最后一个节点移动到了第一个,react 会把前面的节点依次移动,而 vue 只会把最后一个节点移动到最后一个,从这点上来说 vue 的对比方式更加高效。

响应式原理不同

  1. React 主要是通过 setState() 方法来更新状态,状态更新之后,组件也会重新渲染。
  2. vue 会遍历 data 数据对象,使用 Object.definedProperty() 将每个属性都转换为 getter 和 setter,每个 Vue组件实例 都有一个对应的 watcher 实例,在组件初次渲染的时候会记录组件用到了那些数据,当数据发生改变的时候,会触发 setter 方法,并通知所有依赖这个数据的 watcher 实例调用 update 方法去触发组件的 compile 渲染方法,进行渲染数据。

什么是 JSX ?

JSX 是 javascript 的语法扩展。它就像一个拥有 javascript 全部功能的模板语言。它生成 React 元素,这些元素将在 DOM中 呈现。React 建议在组件使用 JSX。在 JSX 中,我们结合了 javascript 和 HTML,并生成了可以在 DOM中 呈现的 react 元素。

函数式组件和类组件

什么是函数式组件(无状态)

函数或无状态组件是一个纯函数,它可接受接受参数,并返回 react 元素。这些都是没有任何副作用的纯函数。这些组件没有状态或生命周期方法

1
2
3
4
5
import { FC } from 'react'; // 其中 FC 是 FunctionComponent 的缩写

const App: FC = () => {
return <h1>Holle React!</h1>
}

什么是类组件(有状态)

类或有状态组件具有状态和生命周期方可能通过 setState() 方法更改组件的状态。类组件是通过扩展 React 创建的。它在构造函数中初始化,也可能有子组件。

1
2
3
4
5
6
7
import { Component } from 'react';

export default class App extends Component {
render() {
return <h1>Holle React!</h1>
}
}

组件三大核心属性之一: State

  1. state 是 React 组件对象中最重要的属性,值是对象(可以包含多个key-value的组合)。
  2. 组件被称为状态机,通过更新组件中的 state 来更新对应的页面显示(重新渲染组件)。
  3. 我们可以理解为 Vue 中的 data。
  4. state 不能直接更改,要通过 setState() 方法更改组件的状态。

初始化 state

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { Component } from 'react';

export default class App extends Component<any,any> {
// 如果声明了 constructor,此处必须使用super接收props。
constructor(props:any){
super(props)
this.state = {name:'React'}
}
// 如果不声明 constructor
// state = {name:'React'}

render() {
return <h1>Holle {this.state.name}!</h1>
}
}

事件绑定和更改 State 状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import { Component } from 'react';

export default class App extends Component<any,any> {
// 如果声明了 constructor,此处必须使用super接收props。
constructor(props:any){
super(props)
this.state = {name:'React'}
}
// 如果不声明 constructor
state = {name:'React'}

// 此处必须要用箭头函数,让this指向外层也就是 APP 的实例,如果直接写changeState(),this会是undefined
changeState = () =>{
// 错误写法
this.state.name = 'Vue'
// 正确写法
this.setState({name:'Vue'})
}

// 如果需要传参
changeState = (data) =>{

}

render() {
// 不需传参,事件不要加上小括号(),事件回调是一个函数,不然就要写成箭头函数的形式
// <h1 onClick={(e)=>this.changeState(e)}>Holle {this.state.name}!</h1>
return <h1 onClick={this.changeState}>Holle {this.state.name}!</h1>
}
}
  1. state 的状态不能直接改变,需要通过 setState() 方法去更改
  2. 事件绑定名称需要使用驼峰法,例如:onClick、onChange、onBlur等
  3. 事件的回调是一个函数,事件的方法要写成箭头函数的形式,让 this 指向类的实例

组件三大核心属性之一: Props

  1. Props 是只读属性,传递给组件以呈现UI和状态。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { Component } from 'react';

class Parent extends Component<any,any> {
state = {name:'我是子组件'}
render() {
const { name } = this.state
return (
<h1>Holle React!</h1>
<Child name={name} />
)
}
}

class Child extends Component<any,any> {
render() {
const { name } = this.props
return <h1>Holle {name}!</h1>
}
}

1656987724187.jpg

PropTypes 为组件提供类型检查

参考文档使用 PropTypes 进行类型检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import { Component } from 'react';
import PropTypes from 'prop-types'

class Parent extends Component<any,any> {
state = {name:'我是子组件'}
render() {
const { name } = this.state
return (
<h1>Holle React!</h1>
<Child name={name} />
)
}
}

class Child extends Component<any,any> {
// 静态类型检查写法,也可使用Child.propTypes写在外部
// 规定 props 类型
static propTypes = {
name:PropTypes.string.isRequired
/* 你可以将属性声明为 JS 原生类型,默认情况下
这些属性都是可选的。
optionalArray: PropTypes.array,
optionalBool: PropTypes.bool,
optionalFunc: PropTypes.func,
optionalNumber: PropTypes.number,
optionalObject: PropTypes.object,
optionalString: PropTypes.string,
optionalSymbol: PropTypes.symbol,

// 任何可被渲染的元素(包括数字、字符串、元素或数组)
// (或 Fragment) 也包含这些类型。
optionalNode: PropTypes.node,

// 一个 React 元素。
optionalElement: PropTypes.element,

// 一个 React 元素类型(即,MyComponent)。
optionalElementType: PropTypes.elementType,

// 你也可以声明 prop 为类的实例,这里使用
// JS 的 instanceof 操作符。
optionalMessage: PropTypes.instanceOf(Message),

// 你可以让你的 prop 只能是特定的值,指定它为
// 枚举类型。
optionalEnum: PropTypes.oneOf(['News', 'Photos']),

// 一个对象可以是几种类型中的任意一个类型
optionalUnion: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.instanceOf(Message)
]),

// 可以指定一个数组由某一类型的元素组成
optionalArrayOf: PropTypes.arrayOf(PropTypes.number),

// 可以指定一个对象由某一类型的值组成
optionalObjectOf: PropTypes.objectOf(PropTypes.number),

// 可以指定一个对象由特定的类型值组成
optionalObjectWithShape: PropTypes.shape({
color: PropTypes.string,
fontSize: PropTypes.number
}),

// An object with warnings on extra properties
optionalObjectWithStrictShape: PropTypes.exact({
name: PropTypes.string,
quantity: PropTypes.number
}),

// 你可以在任何 PropTypes 属性后面加上 `isRequired` ,确保
// 这个 prop 没有被提供时,会打印警告信息。
requiredFunc: PropTypes.func.isRequired,

// 任意类型的必需数据
requiredAny: PropTypes.any.isRequired,

// 你可以指定一个自定义验证器。它在验证失败时应返回一个 Error 对象。
// 请不要使用 `console.warn` 或抛出异常,因为这在 `oneOfType` 中不会起作用。
customProp: function(props, propName, componentName) {
if (!/matchme/.test(props[propName])) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
},

// 你也可以提供一个自定义的 `arrayOf` 或 `objectOf` 验证器。
// 它应该在验证失败时返回一个 Error 对象。
// 验证器将验证数组或对象中的每个值。验证器的前两个参数
// 第一个是数组或对象本身
// 第二个是他们当前的键。
customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
if (!/matchme/.test(propValue[key])) {
return new Error(
'Invalid prop `' + propFullName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
}) */
}
// 规定 props 默认值
static defaultProps = {
name: 'Hello React!'
}
render() {
const { name } = this.props
return <h1>Holle {name}!</h1>
}
}

组件三大核心属性之一: Refs

何时使用 Refs

  1. 管理焦点,文本选择或媒体播放。
  2. 触发强制动画。
  3. 集成第三方 DOM 库。

创建 Refs

  1. 使用 React.createRef() 创建

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import React,{ Component } from 'react';

    export default class App extends Component<any,any> {
    // 创建 Refs
    textInput = React.createRef()
    focusTextInput = () => {
    // 直接使用原生 API 使 text 输入框获得焦点
    // 注意:我们通过 "current" 来访问 DOM 节点
    this.textInput.current.focus();
    }
    render() {
    return (
    <div>
    <h1 onClick={this.focusTextInput}>Holle React!</h1>
    <input type="text" ref={this.textInput}/>
    </div>
    )
    }
    }
  2. 回调 Refs

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import { Component } from 'react';

    export default class App extends Component<any,any> {
    // 创建 Refs
    textInput = null
    setTextInputRef = (e) => {
    // 绑定 Refs
    this.textInput = e
    }
    componentDidMount(){
    // 组件挂载后,让文本框自动获得焦点
    this.textInput && this.textInput.focus()
    }
    render() {
    return <input type="text" ref={this.setTextInputRef}/>
    }
    }
  3. 过时 API: String Refs

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import { Component } from 'react';

    export default class App extends Component<any,any> {
    componentDidMount(){
    // 组件挂载后,让文本框自动获得焦点
    this.refs.textInput.focus()
    }
    render() {
    return <input type="text" ref="textInput"/>
    }
    }

    如果你目前还在使用 this.refs.textInput 这种方式访问 refs ,我们建议用回调函数createRef API 的方式代替。

React 生命周期

什么是生命周期?

  1. 生命周期就是组件从挂载到销毁的一个过程,每个特定的阶段都会有对应的 API 去完成对应的操作
  2. React 组件的生命周期可以分为3个状态:
    • Mounting(挂载):已插入真实 DOM
    • Updating(更新):正在被重新渲染
    • Unmounting(卸载):已移出真实 DOM

src_http___upload-images.jianshu.io_upload_images_5098241-10770cb6c6743070.png_refer_http___upload-images.jianshu.webp

Mounting(挂载)

当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下:

  • constructor(): 在 React 组件挂载之前,会调用它的构造函数。
  • getDerivedStateFromProps(): 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。
  • render(): render() 方法是 class 组件中唯一必须实现的方法。
  • componentDidMount(): 在组件挂载后(插入 DOM 树中)立即调用。
    render() 方法是 class 组件中唯一必须实现的方法,其他方法可以根据自己的需要来实现。

Updating(更新)

当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:

  • getDerivedStateFromProps(): 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。根据 shouldComponentUpdate() 的返回值,判断 React 组件的输出是否受当前 state 或 props 更改的影响。
  • shouldComponentUpdate():当 props 或 state 发生变化时,shouldComponentUpdate() 会在渲染执行之前被调用。
  • render(): render() 方法是 class 组件中唯一必须实现的方法。
  • getSnapshotBeforeUpdate(): 在最近一次渲染输出(提交到 DOM 节点)之前调用。
  • componentDidUpdate(): 在更新后会被立即调用。
    render() 方法是 class 组件中唯一必须实现的方法,其他方法可以根据自己的需要来实现。

Unmounting(卸载)

  • componentDidUpdate(): 在更新后会被立即调用。

声明周期实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import { Component } from 'react';
import ReactDOM from 'react-dom/client'

export default class Count extends Component<any, any>{
/*
1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
1. constructor()
2. getDerivedStateFromProps
3. render()
4. componentDidMount() =====> 常用
一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
1. getDerivedStateFromProps
2. shouldComponentUpdate()
3. render()
4. getSnapshotBeforeUpdate
5. componentDidUpdate()
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
1. componentWillUnmount() =====> 常用
一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
*/
//构造器
constructor(props: any) {
console.log('Count---constructor');
super(props)
//初始化状态
this.state = { count: 0 }
}

//加1按钮的回调
add = () => {
//获取原状态
const { count } = this.state
//更新状态
this.setState({ count: count + 1 })
}

//卸载组件按钮的回调
death = () => {
const root = ReactDOM.createRoot(
document.getElementById('test') as HTMLElement
);
root.unmount()
}

//强制更新按钮的回调
force = () => {
this.forceUpdate()
}

//若state的值在任何时候都取决于props,那么可以使用getDerivedStateFromProps
static getDerivedStateFromProps(props: any, state: any) {
console.log('getDerivedStateFromProps', props, state);
return null
}

//在更新之前获取快照
getSnapshotBeforeUpdate() {
console.log('getSnapshotBeforeUpdate');
return 'atguigu'
}

//组件挂载完毕的钩子
componentDidMount() {
console.log('Count---componentDidMount');
}

//组件将要卸载的钩子
componentWillUnmount() {
console.log('Count---componentWillUnmount');
}

//控制组件更新的“阀门”
shouldComponentUpdate() {
console.log('Count---shouldComponentUpdate');
return true
}

//组件更新完毕的钩子
componentDidUpdate(preProps: any, preState: any, snapshotValue: any) {
console.log('Count---componentDidUpdate', preProps, preState, snapshotValue);
}

render() {
console.log('Count---render');
const { count } = this.state
return (
<div id="test">
<h2>当前求和为:{count}</h2>
<button onClick={this.add}>点我+1</button>
<button onClick={this.death}>卸载组件</button>
<button onClick={this.force}>不更改任何状态中的数据,强制更新一下</button>
</div>
)
}
}

Fragments

React 中的一个常见模式是一个组件返回多个元素。Fragments 允许你将子列表分组,而无需向 DOM 添加额外节点。

1
2
3
4
5
6
7
8
9
render() {
return (
<React.Fragment>
<ChildA />
<ChildB />
<ChildC />
</React.Fragment>
);
}

短语法

1
2
3
4
5
6
7
8
9
10
class Columns extends React.Component {
render() {
return (
<>
<td>Hello</td>
<td>World</td>
</>
);
}
}

组件之间通信

⽗组件向⼦组件通讯

⽗组件可以通过向⼦组件传 props 的⽅式来实现父到子的通讯。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { Component } from 'react';

class Parent extends Component<any,any> {
state = {name:'我是子组件'}
render() {
const { name } = this.state
return (
<div>
<h1>Holle React!</h1>
// 父组件通过 props 向子组件传递数据
<Child name={name} />
</div>
)
}
}

class Child extends Component<any,any> {
render() {
// 子组件接收数据
const { name } = this.props
return <h1>Holle {name}!</h1>
}
}

⼦组件向⽗组件通讯

当⽗组件向⼦组件传递 props 进⾏通讯时,可在该 props 中传递一个回调函数,当⼦组件调⽤该函数时,可将⼦组件中想要传递给父组件的信息作为参数传递给该函数。由于 props 中的函数作⽤域为⽗组件⾃身,因此可以通过该函数内的 setState 更新到⽗组件上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import { Component } from 'react';

class Parent extends Component<any, any> {
receiveData = (data: string) => {
// 子组件向父组件传递的数据
console.log(data)
}
render() {
return (
<div>
<h1>Holle React!</h1>
// 父组件通过 props 向子组件传递数据
<Child receiveData={this.receiveData} />
</div>
)
}
}

class Child extends Component<any, any> {
componentDidMount() {
const msg = '子组件向父组件传递的数据'
this.props.receiveData(msg)
}
render() {
return <h1>Holle React!</h1>
}
}

兄弟组件中通信

  1. 可以利用父子组件中的 props,例如 A 是父组件,B 和 C 都是 A 种的子组件且是兄弟组件,B 可以通过 props 回调传递给 A,然后A再通过 props传递给 C。
  2. 可以考虑下面的通信方式 events 自定义事件通信 和 消息订阅与发布机制

events 自定义事件通信

  1. 安装 events
    1
    2
    yarn add events
    npm i events
  2. 创建一个 events.js 文件,在该文件中生成事件总线:
    1
    2
    import { EventEmitter } from 'events';
    export default new EventEmitter();
  3. 在负责接收数据的组件中,给事件总线身上添加事件:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    import { Component } from 'react'
    import emitter from './utils/event'
    import Child from './Child'

    export default class App extends Component {
    state = { message: '' }
    eventEmitter: any = null
    getData = (message: string) => {
    this.setState({
    message,
    });
    }
    // 组件挂载完成
    componentDidMount() {
    // 组件装载完成以后声明一个自定义事件
    this.eventEmitter = emitter.addListener('childMsg', this.getData);
    }
    // 记得在 componentWillUnmount 钩子中移除 addListener
    componentWillUnmount() {
    emitter.removeListener('childMsg', this.getData);
    }
    render() {
    return (
    <div>
    <h1>子组件B:负责接收数据</h1>
    <p>接收到的数据:{this.state.message}</p>
    <Child />
    </div>
    )
    }
    }
    4. 在负责传递数据的组件中,去调用事件总线身上的事件:
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import emitter from './utils/event'
    import { Component } from 'react'

    export default class ChildA extends Component {
    state = {
    message: '张三'
    }
    sendData = () => {
    emitter.emit('childMsg', this.state.message)
    }
    render() {
    return (
    <div>
    <h1>子组件A:负责传递数据</h1>
    <button onClick={this.sendData}>按钮</button>
    </div>
    )
    }
    }
    1657013477233.jpg

消息订阅与发布机制

  1. 安装 pubsub-js
    1
    2
    yarn add pubsub-js
    npm i pubsub-js
  2. 在组件中发布消息
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    import PubSub from 'pubsub-js'
    import { Component } from 'react'

    export default class ChildA extends Component {
    state = {
    message: '张三'
    }
    sendData = () => {
    // 发布消息
    PubSub.publish('childMsg', this.state.message)
    }
    render() {
    return (
    <div>
    <h1>子组件A:负责传递数据</h1>
    <button onClick={this.sendData}>按钮</button>
    </div>
    )
    }
    }
  3. 组件中订阅消息
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    import { FC, useState, useEffect } from 'react'
    import PubSub from 'pubsub-js'
    import Child from './Child'

    const App: FC = () => {
    const [msg, setMsg] = useState('')
    let PubSubEmitter: any = null
    useEffect(() => {
    // 订阅消息
    PubSubEmitter = PubSub.subscribe('childMsg', (msg, data) => {
    setMsg(data)
    })
    // 组件卸载前取消消息订阅
    return () => {
    PubSub.unsubscribe(PubSubEmitter)
    }
    }, [])
    return (
    <div>
    <h1>子组件B:负责接收数据</h1>
    <p>接收到的数据:{msg}</p>
    <Child />
    </div>
    )
    }
    export default App
    1657013477233.jpg

HOOK

什么是 Hook?

Hook 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。Hook 不能在 class 组件中使用 —— 这使得你不使用 class 也能使用 React。

State Hook

在 useState()中,它接受状态的初始值作为参数,即上例中计数的初始值,它返回一个数组,其中数组第一项为一个变量,指向状态的当前值。类似 this.state,第二项是一个函数,用来更新状态,类似 setState。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { useState,FC } from 'react';

const App: FC = ()=> {
// 声明一个叫 "count" 的 state 变量
const [count, setCount] = useState(0);

return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
export default App

Effect Hook

它可以用来更好的处理副作用,如异步请求等;可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { useState, useEffect, FC } from 'react';

const App: FC = () => {
// 声明一个叫 "count" 的 state 变量
const [count, setCount] = useState(0);
// 更改网页标题
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count])
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
export default App

使用useEffect模拟react三个生命周期

  1. 实现 componentDidMount
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import { useState, useEffect, FC } from 'react';

    const App: FC = () => {
    const [count, setCount] = useState(0);
    useEffect(() => {
    getList() // 调用方法发起异步请求
    }, []);

    return (
    <div>
    hello world
    </div>
    )
    }
  2. 实现 componentDidUpdate
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import { useState, useEffect, FC } from 'react';

    const App: FC = () => {
    const [count, setCount] = useState(0);
    useEffect(() => {
    getList() // 仅在count更改时更新
    }, [count]);

    return (
    <div>
    hello world
    </div>
    )
    }
  3. 实现 componentWillUnmount
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import { useState, useEffect, FC } from 'react';

    const App: FC = () => {
    useEffect(() => {
    getList();
    return () => {
    console.log('组件注销, 实现componentWillUnmount');
    };
    }, []);

    return (
    <div>
    hello world
    </div>
    )
    }

    useEffect的第二个参数集中形式:

    1. 不传: useEffect 会在第一次渲染以及每次更新渲染后都执行。(不建议)
    2. 空数组: useEffect 会在第一次渲染后执行一次。
    3. 由基本类型或者引用类型组成的数组: useEffect 会在第一次渲染以及每次更新渲染后都执行。

Todo