扩展
setState
代码语言:javascript复制(1). setState(stateChange, [callback])------对象式的setState
1.stateChange为状态改变对象(该对象可以体现出状态的更改)
2.callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用
(2). setState(updater, [callback])------函数式的setState
1.updater为返回stateChange对象的函数。
2.updater可以接收到state和props。
3.callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。
总结:
1.对象式的setState是函数式的setState的简写方式(语法糖)
2.使用原则:
(1).如果新状态不依赖于原状态 ===> 使用对象方式
(2).如果新状态依赖于原状态 ===> 使用函数方式
(3).如果需要在setState()执行后获取最新的状态数据,
要在第二个callback函数中读取
LazyLoader
路由组件的懒加载
代码语言:javascript复制# 导入库
import React, {lazy,Suspense} from 'react';
//1.通过React的lazy函数配合import()函数动态加载路由组件 ===> 路由组件代码会被分开打包
const Login = lazy(()=>import('@/pages/Login'))
//2.通过<Suspense>指定在加载得到路由打包文件前显示一个自定义loading界面
<Suspense fallback={<h1>loading.....</h1>}>
<Switch>
<Route path="/xxx" component={Xxxx}/>
<Redirect to="/login"/>
</Switch>
</Suspense>
Hooks
简介
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
useState
代码语言:javascript复制/**
* 使用范围: 用于函数式组件, 使函数式组件具备state的能力
* useState的使用方式
* 1: 从react库中引入 useState 函数
* 2: 使用函数创建值引用和方法引用
* 2.1: const [count, setCount] = useState(0)
* 2.2: 调用useState 入参为初次属性初始化的默认值
* 2.3: 返回值为数组,一般使用结构的方式获取回来, 第一个引用为值对象, 第二个引用为该值对象的赋值函数
* 3: 渲染方式, 直接通过 {count} 渲染
* 4: 赋值方式: 调用赋值函数
* 4.1: 入参为值对象修改 setCount(count 1)
* 4.2: 入参为函数修改: setCount(count => count 1) 函数会有一个入参为当前值对象, 然后需要返回一个新的值对象
*/
import React, {useState} from 'react';
function Index(props) {
const [count, setCount] = useState(0)
const add = () => {
// setCount(count 1)
setCount(count => count 1)
}
return (
<div>
<h2>当前求和为:{count}</h2>
<button onClick={add}> 1</button>
</div>
);
}
export default Index;
useEffect
代码语言:javascript复制/**
* 使用范围: 用于函数式组件, 使函数式组件具备生命周期钩子的能力,可以看做是
* componentDidMount,componentDidUpdate,componentWillUnmount
* 三个生命周期钩子函数的集合
* useEffect的使用方式
* 1: 从react库中引入 useEffect 函数
* 2: 使用函数完成生命周期钩子函数
* -:具体使用看下面注释
*
*/
import React, {useState, useEffect} from 'react';
function Index(props) {
// useState
const [count, setCount] = useState(0)
const [sum, setSum] = useState(0)
const [he, setHe] = useState(0)
/**
* 实现componentDidMount
* useEffect 第二个参数[] 什么也不写, 就是代表不监听任何state的变化, 只有在第一次渲染的时候执行
*/
useEffect(() => {
// setCount(count 1)
// 实现count自动累加
const timer = setInterval(() => {
// 这里有个问题, 需要使用函数式入参, 不能直接使用值入参, 因为值入参是异步的, 函数的话会接受到上一次的值
setCount(count => count 1)
}, 1000)
}, [])
/**
* 实现componentDidMount componentDidUpdate
* useEffect 第二个参数[] 里面写了那些state的值对象, 当这些值对象发生变化时,就会执行这个函数
*/
useEffect(() => {
// 当count改变的时候sum自动加1
if (count !== 0) {
setSum(sum => sum 1)
}
}, [count])
/**
* 实现componentDidMount componentDidUpdate componentWillUnmount
* useEffect 函数, 可以返回一个函数, 这个返回的函数就是componentWillUnmount生命周期钩子, 所有清除定时器,取消订阅等操作就可以写在这个函数里面
*/
useEffect(() => {
// 当count改变的时候sum自动加1
const timer = setInterval(() => {
// 这里有个问题, 需要使用函数式入参, 不能直接使用值入参, 因为值入参是异步的, 函数的话会接受到上一次的值
setHe(he => he 1)
}, 1000)
return () => {
clearInterval(timer)
}
}, [])
return (
<div>
<h2>当前求和为:{count}</h2>
<h2>当前求和为:{sum}</h2>
<h2>当前求和为:{he}</h2>
</div>
);
}
export default Index;
useRef
代码语言:javascript复制/**
* 使用范围: 用于函数式组件, 使函数式组件具备React.createRef的能力
* useRef的使用方式
* 1: 从react库中引入useRef函数
* 2: 使用函数创建属性 const myRef = useRef()
* 3: 绑定到组件 <input ref={myRef} type="text"/>
* 4: 获取值 myRef.current.value
*/
import React, {useRef} from 'react';
function Index(props) {
const myRef = useRef()
const show = () => {
console.log(myRef.current.value)
}
return (
<div>
<input ref={myRef} type="text"/>
<button onClick={show}>显示</button>
</div>
);
}
export default Index;
Fragment
代码语言:javascript复制/**
* Fragment : 代码片段标签, 在React渲染时会被丢弃
* 使用方式:
* 1: 从react库中引入
* 2: 一般包裹在最外层
* 3: 只接受唯一一个属性 key
* 4: 如不过想写, 可以使用空标签替换 <></>
*/
import React, {Fragment} from 'react';
function Index(props) {
return (
<Fragment key={'fg'}>
<h2>代码片段:</h2>
</Fragment>
);
}
export default Index;
Context useContext(Hooks)
代码语言:javascript复制/**
* Context: 上下文对象, 一般用于多层次组件传递值
* 使用方式:
* 1: 从react中引入React
* 2: 创建: const UserNameContext = createContext('dance')
* -: 看下方注释
*/
import React, {Component, useState, useContext, createContext} from 'react';
// 创建Context对象
const UserNameContext = createContext('dance')
function Main(props) {
const [userName, setUserName] = useState('tom');
return (
<>
<h2>我是Main组件</h2>
<h3>我的用户名是:{userName}</h3>
<hr/>
{/* 通过value属性传入参数, 所有的子组件就都可以获取到context */}
<UserNameContext.Provider value={userName}>
<A/>
</UserNameContext.Provider>
</>
);
}
function A(props) {
return (
<>
<h2>我是A组件</h2>
{/* 通过标签Consumer获取,并渲染 */}
<UserNameContext.Consumer>
{
value => (<h3>接受到的用户名是:{value}</h3>)
}
</UserNameContext.Consumer>
<hr/>
<B/>
</>
);
}
/**
* 函数式组件接受Context
*/
function B(props) {
// 通过useContext函数获取,并渲染
let context = useContext(UserNameContext)
return (
<>
<h2>我是B组件</h2>
<h3>接受到的用户名是:{context}</h3>
<C/>
</>
);
}
/**
* 类组件 接受Context
*/
class C extends Component {
// 类组件通过属性获取,并渲染
static contextType = UserNameContext
render() {
return (
<>
<h2>我是C组件</h2>
<h3>接受到的用户名是:{this.context}</h3>
</>
);
}
}
export default Main;
组件优化
Component的两个问题
- 只要执行setState(),即使不改变状态数据, 组件也会重新render() ==> 效率低
- 只当前组件重新render(), 就会自动重新render子组件,纵使子组件没有用到父组件的任何数据 ==> 效率低
优化
要让组件, 只有当组件的state或props数据发生改变时才重新render()
因为Component中的shouldComponentUpdate()总是返回true
解决办法
代码语言:javascript复制办法1:
重写shouldComponentUpdate()方法
比较新旧state或props数据, 如果有变化才返回true, 如果没有返回false
办法2:
使用PureComponent
PureComponent重写了shouldComponentUpdate(), 只有state或props数据有变化才返回true
注意:
只是进行state和props数据的浅比较, 如果只是数据对象内部数据变了, 返回false
不要直接修改state数据, 而是要产生新数据
项目中一般使用PureComponent来优化
但是一般项目开发中都是用函数式组件 hooks来写的
render props
如何向组件内部动态传入带内容的结构(标签)?
代码语言:javascript复制Vue中:
使用slot技术, 也就是通过组件标签体传入结构 <A><B/></A>
React中:
使用children props: 通过组件标签体传入结构
使用render props: 通过组件标签属性传入结构,而且可以携带数据,一般用render函数属性
children props
代码语言:javascript复制<A>
<B>xxxx</B>
</A>
{this.props.children}
问题: 如果B组件需要A组件内的数据, ==> 做不到
render props
代码语言:javascript复制<A render={(data) => <C data={data}></C>}></A>
A组件: {this.props.render(内部state数据)}
C组件: 读取A组件传入的数据显示 {this.props.data}
错误边界
- 理解:
- 错误边界(Error boundary):用来捕获后代组件错误,渲染出备用页面
- 特点:
- 只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误
- 使用方式:
- getDerivedStateFromError配合componentDidCatch
// 生命周期函数,一旦后台组件报错,就会触发
static getDerivedStateFromError(error) {
console.log(error);
// 在render之前触发
// 返回新的state
return {
hasError: true,
};
}
componentDidCatch(error, info) {
// 统计页面的错误。发送请求发送到后台去
console.log(error, info);
}
组件通信方式总结
组件间的关系:
- 父子组件
- 兄弟组件(非嵌套组件)
- 祖孙组件(跨级组件)
几种通信方式:
代码语言:javascript复制1.props:
(1).children props
(2).render props
2.消息订阅-发布:
pubs-sub、event等等
3.集中式管理:
redux、dva等等
4.conText:
生产者-消费者模式
比较好的搭配方式:
代码语言:javascript复制父子组件:props
兄弟组件:消息订阅-发布、集中式管理
祖孙组件(跨级组件):消息订阅-发布、集中式管理、conText(开发用的少,封装插件用的多)