八、React 扩展
大约 6 分钟约 1865 字
(一)setState 更新状态的两种写法
setState
1.对象式的 setState(stateChange, [callback])
stateChange
为状态改变对象,该对象可以体现出状态的更改callback
是可选的回调函数,它在状态更新完毕、界面也更新后(render 调用后)才被调用
const { count } = this.state;
// this.setState({ count: count + 1 });
// setState的调用是同步的,但是后续引起React更新的动作是异步的
// console.log("13行的输出", this.state.count); // 0
// callback是React更新状态后被调用
this.setState(
{
count: count + 1,
},
() => {
console.log(this.state.count); // 1
},
);
setState
2.函数式的 setState(updater, [callback])
updater
为返回的stateChange
对象的函数updater
可以接收到state
和props
callback
是可选的回调函数,它在状态更新、界面也更新后(render 调用后)才被调用
this.setState(
(state, props) => ({
count: state.count + props.x * 1,
}),
() => {
console.log(this.state.count);
},
);
3.总结
- 对象式的
setState
是函数式的setState
的简写方式(语法糖) - 使用原则
- 如果新状态不依赖于原状态
- 使用对象方式
- 如果新状态依赖于原状态
- 使用函数方式
- 如果需要在
setState()
执行后获取最新的状态数据- 要在第二个
callback
函数中读取
- 要在第二个
- 如果新状态不依赖于原状态
// 依赖于原状态也可以用对象式
this.setState({
count: this.state.count + 1,
});
lazyLoad
—— 路由组件的懒加载
(二)lazy()
1.- 通过 React 的
lazy
函数配合import()
函数动态加载路由组件 - 路由组件代码会被分开打包
const Login = lazy(() => import("@/pages/Login"));
<Suspense>
2.- 通过
<Suspense>
指定在加载得到路由打包文件前显示一个自定义loading
界面
<Suspense fallback={<h1>Loading...</h1>}>
<Switch>
<Route path="/xxx" component={Xxxx} />
<Redirect to="/login" />
</Switch>
</Suspense>
<Suspense fallback={<Loading />}>
<Switch>
<Route path="/xxx" component={Xxxx} />
<Redirect to="/login" />
</Switch>
</Suspense>
(三)Hooks
1.React Hook/Hooks 是什么?
Hook
是 React 16.8.0 版本增加的新特性/新语法- 可以在函数组件中使用
state
以及其他的 React 特性
2.三个常用的 Hook
- State Hook:
React.useState()
- Effect Hook:
React.useEffect()
- Ref Hook:
React.useRef()
3.State Hook
1)作用
- 使函数组件也可以有
state
状态,并进行状态数据的读写操作
2)语法
const [xxx, setXxx] = React.useState(initValue);
3)说明
- 参数:第一次初始化指定的值在内部作缓存
- 返回值:包含 2 个元素的数组,第 1 个为内部当前状态值,第 2 个为更新状态值的函数
4)setXxx()两种写法
setXxx(newValue)
- 参数为非函数值,直接指定新的状态值,内部用其覆盖原来的状态值
setXxx(value => newValue)
- 参数为函数值,接收原本的状态值,返回新的状态值,内部用其覆盖原来的状态值
4.Effect Hook
1)作用
- 使函数组件中可以执行副作用操作
- 用于模拟类组件中的生命周期钩子
2)React 中的副作用操作
- 发送 ajax 请求获取数据
- 设置订阅消息、启动定时器
- 手动更改真实 DOM
3)语法和说明
useEffect(() => {
// 在此可以执行任何带副作用操作
return () => {
// 在组件卸载前执行
// 在此做一些收尾工作, 比如清除定时器/取消订阅等
};
}, [stateValue]); // 如果指定的是[], 回调函数只会在第一次render()后执行
提示
可以把 useEffect()
看做如下三个函数的组合
componentDidMount()
componentDidUpdate()
componentWillUnmount()
5.Ref Hook
1)作用
- 使函数组件中可以存储、查找组件内的标签或任意其他数据
2)语法
const refContainer = useRef();
3)备注
- 保存标签对象,功能与
React.createRef()
一样
(四)Fragment
1.语法
1)写法一:只能有一个 key 属性
<Fragment key={key}></Fragment>
2)写法二:不能有任何属性
<></>
2.作用
- 组件中不用必须包裹一个真实的 DOM 根标签
(五)Context
1.理解
- 一种组件间通信方式
- 常用于【祖组件】和【后代组件】间通信
2.使用
Context
容器对象
1)创建 - 该对象必须定义在通信的祖孙组件都能访问到的作用域中
const XxxContext = React.createContext();
XxxContext.Provider
,通过 value
属性给后代组件传递数据
2)渲染子组件时,外面包裹 <XxxContext.Provider value={数据}>
<子组件 />
</XxxContext.Provider>
3)后代组件读取数据
// 方式一:仅适用于类组件
static contextType = xxxContext; // 声明接收context
{this.context} // 读取context中的value数据
// 方式二:函数组件与类组件都可以
<xxxContext.Consumer>
{(value) =>
// value就是context中的value数据
要显示的内容
}
</xxxContext.Consumer>
3.注意
- 在应用开发中一般不用
context
- 一般都用它的封装 React 插件
(六)组件优化
1.Component 的 2 个问题
- 只要执行
setState()
,即使不改变状态数据,组件也会重新render
- 效率低
- 只要当前组件重新
render
,即使子组件没有用到父组件的任何问题,也会自动重新render
子组件- 效率低
2.说明
- 因为
Component
中的shouldComponentUpdate()
总是返回true
,所以有上述问题 - 要想提高效率,只有当组件的
state
或props
数据发生改变时才重新render
3.解决
1)方法一
- 重写
shouldComponentUpdate()
方法 - 比较新旧
state
或props
数据,有变化才返回true
,否则返回false
2)方法二
- 使用
PureComponent
PureComponent
重写了shouldComponentUpdate()
方法- 只有
state
或props
数据有变化才返回true
警告
- 只是进行
state
和props
数据的浅比较,如果只是数据对象内部数据变了,返回false
- 不要直接修改
state
数据,而是要产生新数据 - 项目中一般使用
PureComponent
来优化
(七)render props
1.如何向组件内部动态传入带内容的结构(标签)?
1)Vue 中
- 使用
slot
技术,也就是通过组件标签体传入结构
<A>
<B></B>
</A>
2)React 中
- 使用
children
props- 通过组件标签体传入结构
- 使用
render
props- 通过组件标签属性传入结构,而且可以携带数据,一般用
render
函数属性
- 通过组件标签属性传入结构,而且可以携带数据,一般用
2.children props
- 问题:如果 B 组件需要 A 组件内的数据,无法实现
<A>
<B>xxxxx</B>
</A>;
{
this.props.children;
}
3.render props
<A
render={(data) => {
<C data={data}></C>;
}}
></A>;
// A组件
{
this.props.render(内部state数据);
}
// C组件读取A组件传入的数据并显示
{
this.props.data;
}
(八)错误边界
1.理解
- 错误边界(Error Boundary)
- 用来捕获后代组件错误,渲染出备用页面,类比
getDerivedStateFromProps
理解
2.特点
- 只能捕获 后代组件生命周期 产生的错误
- 不能捕获自己组件产生的错误
- 不能捕获其他组件在合成事件、定时器中产生的错误
3.使用方式
getDerivedStateFromError
配合componentDidCatch
使用
// 生命周期函数,一旦后台组件报错就会触发
static getDerivedStateFromError(error) {
console.log(error);
// 在render之前触发
// 返回新的state
return {
hasError: true,
}
}
componentDidCatch(error, info) {
// 统计页面的错误,发送请求到后台
console.log(error, info);
}
(九)组件间通信方式总结
1.组件间的关系
- 父子组件
- 兄弟组件(非嵌套组件)
- 祖孙组件(跨级组件)
2.几种通信方式
1)props
children
propsrender
props
2)消息订阅-发布
- pub-sub
- event
- ......
3)集中式管理
- redux
- dva
- ......
4)context
- 生产者-消费者模式
3.比较好的搭配方式
- 父子组件 —— props
- 兄弟组件 —— 消息订阅-发布、集中式管理
- 祖孙组件 —— 消息订阅-发布、集中式管理、context(开发用得少,封装插件用的多)