React拓展
setState
对象式
setState(stateChange, [callback])
------对象式的 setState
, setState
引起的后续动作是异步的。
state = { count: 0 }
add = () => {
this.setState({count: count + 1})
console.log(this.state.count) // 这里输出0,因为setState异步操作还没执行
}
stateChange
为状态改变对象(该对象可以体现出状态的更改)。callback
是可选的回调函数, 它在状态更新完毕、界面也更新后(render
调用后)才被调用。state = { count: 0 } add = () => { this.setState({count: count + 1}, () => { console.log(this.state.count) // 1 }) }
函数式
setState(updater, [callback])
------函数式的 setState
。
updater
为返回stateChange
对象的函数。updater
可以接收到state
和props
。callback
是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。
state = { count: 0 }
add = () => {
this.setState((state, props) => {
return {count: state.count + 1} // 可以通过形参获取state内的值
})
}
总结
- 对象式的
setState
是函数式的setState
的简写方式(语法糖)。 - 使用原则:
- 如果新状态不依赖于原状态 ===> 使用对象方式。
- 如果新状态依赖于原状态 ===> 使用函数方式。
- 如果需要在
setState()
执行后获取最新的状态数据,要在第二个callback
函数中读取。
lazyLoad
路由组件无论看与不看都已经都引进来了,可以使用路由懒加载优化。
使用方法:
-
引入
lazy
函数import {lazy} from 'react'
-
路由调用
lazy
函数通过
React
的lazy
函数配合import()
函数动态加载路由组件 ===> 路由组件代码会被分开打包。const Home = lazy(() => import('./Home'))
-
引入
Suspense
组件通过
<Suspense>
指定在加载得到路由打包文件前显示一个自定义loading
界面。import {lazy, Suspense} from 'react' import Loading from './Loading' // loading组件不能用懒加载了 <Suspense fallback={<Loading />}> <Route path="./home" component={Home}></Route> </Suspense>
hooks
函数式组件在 16.8 版本前没有自己的 this
指向,因此使用不了 state
和 action
,因此没有类式组件好用。
是什么
Hook
是React 16.8.0
版本增加的新特性/新语法。- 可以让你在函数组件中使用
state
以及其他的React
特性。
State Hook
State Hook
让函数组件也可以有 state
状态,并进行状态数据的读写操作。
语法: const [xxx, setXxx] = React.useState(initValue)
useState()
说明:
- 参数:第一次初始化指定的值在内部作缓存。
- 返回值:包含2个元素的数组, 第1个为内部当前状态值, 第2个为更新状态值的函数。
import React from 'react'
function Demo() {
const [count, setCount] = React.useState(0) // 定义state的值,每一次调用都会执行,如果没有count,赋初始值0,如果有,则缓存这个值,覆盖0
add = () => {
setCount(count + 1)
}
return (
<div>{count}</div>
<button onClick={add}></button>
)
}
setXxx()
2种写法:
setXxx(newValue)
: 参数为非函数值,直接指定新的状态值,内部用其覆盖原来的状态值。setXxx(value => newValue)
:参数为函数,接收原本的状态值, 返回新的状态值,内部用其覆盖原来的状态值。add = () => { setCount(count => count + 1) }
Effect Hook
Effect Hook
可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)。
语法和说明:
useEffect(() => {
// 在此可以执行任何带副作用操作
return () => { // 在组件卸载前执行
// 在此做一些收尾工作, 比如清除定时器/取消订阅等
}
}, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行
[stateValue]
是一个可选参数,如果都不写,state的值发生变化也会触发。如果设为空数组,谁也不监测,挂载时触发一次。
import React from 'react'
function Demo() {
React.useEffect(() => {
console.log('@')
}, []) // 类似componentDidMount钩子函数,挂载时触发一次
return (
<div></div>
)
}
总结:
可以把 useEffect Hook 看做如下三个函数的组合:
componentDidMount()
:第二个参数为[]componentDidUpdate()
:第二个参数不填componentWillUnmount()
:在第一个参数中返回一个函数。
Ref Hook
Ref Hook
可以在函数组件中存储/查找组件内的标签或任意其它数据。
语法:
const refContainer = useRef()
作用:保存标签对象,功能与React.createRef()
一样。
Fragment
作用:可以不用必须有一个真实的DOM根标签了
使用
import {Fragment} from 'react'
<Fragment><Fragment>
<></>
Context
一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信。
使用
- 创建Context容器对象:
const XxxContext = React.createContext()
- 渲染子组时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:
<xxxContext.Provider value={数据}> 子组件 </xxxContext.Provider>
- 后代组件读取数据:
//第一种方式:仅适用于类组件 static contextType = xxxContext // 声明接收context this.context // 读取context中的value数据 第二种方式: 函数组件与类组件都可以 <xxxContext.Consumer> { value => ( // value就是context中的value数据 要显示的内容 ) } </xxxContext.Consumer>
注意:
在应用开发中一般不用
context
, 一般都用它的封装react
插件。
Component的2个问题
- 只要执行
setState()
,即使不改变状态数据,组件也会重新render()
==> 效率低。- 只当前组件重新
render()
,就会自动重新render
子组件,纵使子组件没有用到父组件的任何数据 ==> 效率低
当组件的state或props数据发生改变时才重新render()。
解决办法:
-
重写
shouldComponentUpdate()
方法比较新旧
state
或props
数据, 如果有变化才返回true
, 如果没有返回false
。 -
使用
PureComponent
PureComponent
重写了shouldComponentUpdate()
,只有state
或props
数据有变化才返回true
。import {PureComponent} from 'react' class Child extends PureComponent {}
注意:
- 只是进行
state
和props
数据的浅比较, 如果只是数据对象内部数据变了, 返回false
。- 不要直接修改
state
数据, 而是要产生新数据,因为对象的地址没有发生改变。- 项目中一般使用
PureComponent
来优化。
render props
如何向组件内部动态传入带内容的结构(标签)?
Vue中:使用slot技术, 也就是通过组件标签体传入结构
<A><B/></A>
React中:
-
使用
children props
:通过组件标签体传入结构。<A> <B>xxxx</B> </A> {this.props.children}
问题: 如果B组件需要A组件内的数据, ==> 做不到
-
使用
render props
:通过组件标签属性传入结构,而且可以携带数据,一般用render函数属性<A render={(data) => <C data={data}></C>}></A>
A组件:
{this.props.render(内部state数据)}
C组件: 读取A组件传入的数据显示
{this.props.data}
组件通信
父子组件
props:children props;render props
兄弟组件
消息订阅-发布pubs-sub、集中式管理redux
祖孙组件
消息订阅-发布、集中式管理、context