React扩展
setState
setState
更新状态的2种写法:
setState(stateChange, [callback])
------对象式的setState
stateChange
为状态改变对象(该对象可以体现出状态的更改)callback
是可选的回调函数, 它在状态更新完毕、界面也更新后(render
调用后)才被调用
setState(updater, [callback])
------函数式的setState
updater
为返回stateChange
对象的函数updater
可以接收到state
和props
callback
是可选的回调函数, 它在状态更新、界面也更新后(render
调用后)才被调用
1 | import React, { Component } from 'react' |
-
对象式的
setState
是函数式的setState
的简写方式(语法糖) -
使用原则:
(1).如果新状态不依赖于原状态 => 使用对象方式
(2).如果新状态依赖于原状态 => 使用函数方式
(3).如果需要在
setState()
执行后获取最新的状态数据,要在第二个callback
函数中读取
lazyLoad
路由组件的懒加载:
- 通过
React
的lazy
函数配合import()
函数动态加载路由组件 => 路由组件代码会被分开打包 - 通过
<Suspense fallback=?><Suspense/>
指定在加载得到路由打包文件前显示一个自定义loading界面
1 | import React, { Component, lazy, Suspense } from 'react' |
Hooks
Hook是React 16.8.0版本增加的新特性/新语法,可以在函数组件中使用 state
以及其他的 React 特性
State Hook
State Hook让函数组件也可以有state状态,并进行状态数据的读写操作
- 语法:
const [xxx, setXxx] = React.useState(initValue)
useState()
说明:- 参数: 第一次初始化指定的值在内部作缓存
- 返回值: 包含2个元素的数组, 第1个为内部当前状态值, 第2个为更新状态值的函数
setXxx()
2种写法:setXxx(newValue)
: 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值setXxx(value => newValue)
: 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值
1 | import React from 'react' |
Effect Hook
Effect Hook 可以在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)
-
React中的副作用操作:
* 发ajax请求数据获取 * 设置订阅 / 启动定时器 * 手动更改真实DOM
-
语法和说明:
1
2
3
4
5
6useEffect(() => {
// 在此可以执行任何带副作用操作
return () => { // 在组件卸载前执行
// 在此做一些收尾工作, 比如清除定时器/取消订阅等
}
}, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行,即什么都不监听 -
可以把
useEffect
Hook 看做如下三个函数的组合
*componentDidMount()
*componentDidUpdate()
*componentWillUnmount()
1 | import React from 'react' |
Ref Hook
Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据
- 语法:
const refContainer = useRef()
- 作用:保存标签对象,功能与
React.createRef()
一样
1 | import React from 'react' |
Fragment
<Fragment></Fragment>
可以传key
和children
属性,<></>
不允许传任何属性
像这样:
1 | import React, { Component, Fragment } from 'react' |
Context
一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信
在应用开发中一般不用context, 一般都用它的封装react插件
-
创建Context容器对象:
const XxxContext = React.createContext()
-
渲染子组件时,外面包裹
XxxContext.Provider
, 通过value属性给后代组件传递数据:1
2
3<xxxContext.Provider value={数据}>
子组件
</xxxContext.Provider> -
后代组件读取数据:
1
2
3
4
5
6
7
8
9
10
111. //第一种方式:仅适用于类组件
static contextType = xxxContext // 声明接收context
this.context // 读取context中的value数据
2. //第二种方式: 函数组件与类组件都可以
<xxxContext.Consumer>
{
value => ( // value就是context中的value数据
要显示的内容
)
}
</xxxContext.Consumer>
1 | import React, { Component } from 'react' |
组件优化
Component的2个问题
- 只要执行
setState()
,即使不改变状态数据,组件也会重新render()
=> 效率低 - 只要前组件重新
render()
,就会自动重新render
子组件,纵使子组件没有用到父组件的任何数据 => 效率低 - 原因:
Component
中的shouldComponentUpdate()
总是返回true
效率高的做法
只有当组件的state
或props
数据发生改变时才重新render()
-
重写
shouldComponentUpdate()
方法,比较新旧state
或props
数据, 如果有变化才返回true
,如果没有返回false
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
50import React, { Component } from 'react'
export default class Parent extends Component {
state = {carName: '奔驰'}
changeCar = () => {
this.setState({carName: '迈巴赫'})
}
shouldComponentUpdate(nextProps, nextState) {
console.log(nextProps, nextState) // 接下来要变化的目标props和目标state
console.log(this.props, this.state) // 目前的props和state
if(JSON.stringify(this.state) === JSON.stringify(nextState)) {
return false
} return true
}
render() {
console.log('parent--render')
const {carName} = this.state
return (
<div style={{ 'width': '500px', 'backgroundColor': 'skyblue', 'padding': '10px' }}>
<h3>这是Parent组件</h3>
<span>车名字是: {carName}</span>
<br /><button onClick={this.changeCar}>点击换车</button>
<Child carName='宝马'/>
</div>
)
}
}
class Child extends Component {
shouldComponentUpdate(nextProps, nextState) {
console.log(nextProps, nextState) // 接下来要变化的目标props和目标state
console.log(this.props, this.state) // 目前的props和state
if(JSON.stringify(this.props) === JSON.stringify(nextProps)) {
return false
} return true
}
render() {
console.log('child--render')
return (
<div style={{ 'width': '100%', 'backgroundColor': 'orange', 'padding': '10px', 'marginTop': '20px' }}>
<h3>这是Child组件</h3>
<span>车名字是: {this.props.carName}</span>
</div>
)
}
} -
使用
PureComponent
,PureComponent
重写了shouldComponentUpdate()
, 只有state
或props
数据有变化才返回true
项目中一般使用PureComponent
来优化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
35import React, { PureComponent } from 'react'
export default class Parent extends PureComponent {
state = {carName: '奔驰'}
changeCar = () => {
this.setState({carName: '迈巴赫'})
}
render() {
console.log('parent--render')
const {carName} = this.state
return (
<div style={{ 'width': '500px', 'backgroundColor': 'skyblue', 'padding': '10px' }}>
<h3>这是Parent组件</h3>
<span>车名字是: {carName}</span>
<br /><button onClick={this.changeCar}>点击换车</button>
<Child carName='宝马'/>
</div>
)
}
}
class Child extends PureComponent {
render() {
console.log('child--render')
return (
<div style={{ 'width': '100%', 'backgroundColor': 'orange', 'padding': '10px', 'marginTop': '20px' }}>
<h3>这是Child组件</h3>
<span>车名字是: {this.props.carName}</span>
</div>
)
}
}PureComponent
只是进行state
和props
数据的浅比较,如果只是数据对象内部数据变了, 返回false
,不要直接修改state
数据, 而是要产生新数据
render props
向组件内部动态传入带内容的结构(标签):
-
Vue
中:- 使用slot技术, 也就是通过组件标签体传入结构
<A><B/></A>
- 使用slot技术, 也就是通过组件标签体传入结构
-
React
中:-
使用
children props
: 通过组件标签体传入结构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
37import React, { Component } from 'react'
export default class Parent extends Component {
render() {
return (
<div style={{ 'width': '500px', 'backgroundColor': 'skyblue', 'padding': '10px' }}>
<h3>这是Parent组件</h3>
<A><B/></A>
</div>
)
}
}
class A extends Component {
state = {name: 'tom'}
render() {
console.log(this)
const {name} = this.state
return (
<div style={{ 'width': '100%', 'backgroundColor': 'orange', 'padding': '10px' }}>
<h3>这是A组件</h3>
{this.props.children}
</div>
)
}
}
class B extends Component {
render() {
console.log('render--B')
return (
<div style={{ 'width': '100%', 'backgroundColor': 'rgb(79, 207, 93)', 'padding': '10px' }}>
<h3>这是B组件</h3>
</div>
)
}
} -
使用
render props
: 通过组件标签属性传入结构,而且可以携带数据,一般用render
函数属性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
37import React, { Component } from 'react'
export default class Parent extends Component {
render() {
return (
<div style={{ 'width': '500px', 'backgroundColor': 'skyblue', 'padding': '10px' }}>
<h3>这是Parent组件</h3>
<A render={name => <B name={name}/>} />
</div>
)
}
}
class A extends Component {
state = {name: 'tom'}
render() {
console.log(this)
const {name} = this.state
return (
<div style={{ 'width': '100%', 'backgroundColor': 'orange', 'padding': '10px' }}>
<h3>这是A组件</h3>
{this.props.render(name)}
</div>
)
}
}
class B extends Component {
render() {
console.log('render--B')
return (
<div style={{ 'width': '100%', 'backgroundColor': 'rgb(79, 207, 93)', 'padding': '10px' }}>
<h3>这是B组件, {this.props.name}</h3>
</div>
)
}
}
-
错误边界
错误边界(Error boundary
):用来捕获后代组件错误,渲染出备用页面,只使用于生产环境
只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误
1 | import React, { Component } from 'react' |
组件间通信方式总结
组件间的关系
- 父子组件
- 兄弟组件(非嵌套组件)
- 祖孙组件(跨级组件)
几种通信方式
props
children props
render props
- 消息订阅-发布
pub-sub
、event
等
- 集中式管理
redux
、dva
等
conText
- 生产者-消费者模式
比较好的搭配方式
- 父子组件:
props
- 兄弟组件:消息订阅-发布、集中式管理
- 祖孙组件(跨级组件):消息订阅-发布、集中式管理、
conText
(开发用的少,封装插件用的多)