使用setState
更新状态有 2 种写法。
对象式
是函数式
的简写方式(语法糖)。
使用原则(非必须):
对象式
函数式
setState()
执行后获取最新的状态数据,要在callback
函数中读取setState(stateChange, [callback]);
stateChange
为状态改变对象(该对象可以体现出状态的更改)callback
是可选的回调函数, 它在状态更新完毕、界面也更新后(render
调用后)才被调用异步
的,如果想要查看更新后的状态
,需要写在callback
中const {
count } = this.state;
this.setState({
count: count + 1 }, () => {
console.log(this.state.count);
});
setState(updater, [callback]);
updater
为返回stateChange
对象的函数,可以接收到state
和props
callback
是可选的回调函数, 它在状态更新完毕、界面也更新后(render
调用后)才被调用
this.setState((state, props) => ({ count: state.count + 1 }));
通过 React 的lazy
函数配合import()
函数动态加载路由组件,使路由组件代码分开打包。
import Loading from './Loading';
const Home = lazy(() => import('./Home'));
const About = lazy(() => import('./About'));
通过
标签指定在加载得到路由打包文件前显示一个自定义loading界面
。
<Suspense fallback={
<Loading />}>
{
/* 注册路由 */}
<Route path="/about" component={
About} />
<Route path="/home" component={
Home} />
</Suspense>
Hook
是 React 16.8.0 版本增加的新特性,可以在函数组件
中使用state
以及其他的 React 特性。下面介绍三个常用的Hook
:
React.useState()
React.useEffect()
React.useRef()
State Hook 让函数组件也可以有state
状态,并进行状态数据的读写操作。
const [xxx, setXxx] = React.useState(initValue); // 解构赋值
useState()
参数:第一次初始化指定的值在内部作缓存
返回值:包含 2 个元素的数组,第 1 个为内部当前状态值,第 2 个为更新状态值的函数
setXxx()
2 种写法
setXxx(newValue)
:参数为非函数值,直接指定新的状态值,内部用其覆盖原来的状态值
setXxx(value => newValue)
:参数为函数,接收原本的状态值,返回新的状态值,内部用其覆盖原来的状态值
function Demo() {
const [count, setCount] = React.useState(0);
//加的回调
function add() {
// 第一种写法
// setCount(count + 1);
// 第二种写法
setCount(count => count + 1);
}
return (
<div>
<h2>当前求和为:{
count}</h2>
<button onClick={
add}>点我+1</button>
</div>
);
}
Effect Hook 可以在函数组件中执行副作用操作
(用于模拟类组件中的生命周期钩子)。
React 中的副作用操作
:
ajax
请求数据获取useEffect(() => {
// 在此可以执行任何带副作用操作
// 相当于componentDidMount()
return () => {
// 在组件卸载前执行
// 在此做一些收尾工作, 比如清除定时器/取消订阅等
// 相当于componentWillUnmount()
};
}, [stateValue]); // 监听stateValue
// 如果省略数组,则检测所有的状态,状态有更新就又调用一次回调函数
// 如果指定的是[], 回调函数只会在第一次render()后执行
可以把useEffect()
看做如下三个函数的组合:
componentDidMount()
componentDidUpdate()
componentWillUnmount()
function Demo() {
const [count, setCount] = React.useState(0);
React.useEffect(() => {
let timer = setInterval(() => {
setCount(count => count + 1);
}, 500);
console.log('@@@@');
return () => {
clearInterval(timer);
};
}, [count]);
// 检测count的变化,每次变化,都会输出'@@@@'
//加的回调
function add() {
// 第一种写法
// setCount(count + 1);
// 第二种写法
setCount(count => count + 1);
}
// 卸载组件的回调;
function unmount() {
ReactDOM.unmountComponentAtNode(document.getElementById('root'));
}
return (
<div>
<h2>当前求和为:{
count}</h2>
<button onClick={
add}>点我+1</button>
<button onClick={
unmount}>卸载组件</button>
</div>
);
}
Ref Hook 可以在函数组件中存储/查找组件内的标签或任意其它数据。
保存标签对象,功能与React.createRef()
一样
const refContainer = useRef();
function Demo() {
const myRef = React.useRef();
//提示输入的回调
function show() {
alert(myRef.current.value);
}
return (
<div>
<input type="text" ref={
myRef} />
</div>
);
}
使用
后,可以不用必须有一个真实的 DOM 根标签了。
import React, {
Component, Fragment } from 'react';
export default class Demo extends Component {
render() {
return (
<Fragment key={
1}>
<input type="text" />
<input type="text" />
</Fragment>
);
}
}
使用空标签<>>
包裹也可以,他们的区别如下:
:可以接收key
属性,不能接收其他属性<>>
:不能接受属性一种组件间通信方式,常用于祖组件
与后代组件
间通信。
在组件外部创建Context
容器对象:
const XxxContext = React.createContext();
渲染子组时,外面包裹xxxContext.Provider
,通过value
属性给后代组件传递数据:
<XxxContext.Provider value={
数据}>
子组件
</XxxContext.Provider>
后代组件读取数据:
方式(1),仅适用于类组件
:
static contextType = xxxContext // 声明接收context
console.log(this.context); // 读取context中的value数据
方式(2),函数组件
与类组件
都可以:
<XxxContext.Consumer>
{
value => `${
value.username},年龄是${
value.age}`}
</XxxContext.Consumer>
React 中Component
组件的2个问题:
setState()
,即使不改变状态数据,组件也会重新render()
render
,就会自动重新render
子组件,即使子组件没有发生任何变化,这导致页面更新的效率低下效率高的做法:
state
或props
数据发生改变时才重新render()
。问题的原因:
Component
中的shouldComponentUpdate()
总是返回true
解决办法:
重写shouldComponentUpdate()
方法
比较新旧state
或props
数据,如果有变化才返回true
,否则返回false
使用PureComponent
组件代替Component
组件
PureComponent
重写了shouldComponentUpdate()
,只有state
或props
数据有变化才返回true
只是进行state
和props
数据的浅比较
,如果只是数据对象内部数据变了,返回false
所以不要直接修改state
数据,而是要产生新数据
向组件内部动态传入带内容的结构(标签)。
<A>
<B>xxxx</B>
</A>
B
组件通过this.props.children
获取便签中的数据。
但是B
组件获得不到A
组件内的数据。
export default class Parent extends Component {
render() {
return (
<div className="parent">
<h3>我是Parent组件</h3>
<A render={
name => <B name={
name} />} />
</div>
);
}
}
class A extends Component {
state = {
name: 'tom' };
render() {
console.log(this.props);
const {
name } = this.state;
return (
<div className="a">
<h3>我是A组件</h3>
{
this.props.render(name)}
</div>
);
}
}
class B extends Component {
render() {
console.log('B--render');
return (
<div className="b">
<h3>我是B组件,{
this.props.name}</h3>
</div>
);
}
}
相当于A
组件内部写了个插槽,可以在Parent
组件中任意更改向插槽中插入的组件,并传递A
组件的数据。
用来捕获后代组件错误,渲染出备用页面。
只能捕获后代组件生命周期
产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误。
getDerivedStateFromError
+ componentDidCatch
export default class Parent extends Component {
state = {
hasError: '', // 用于标识子组件是否产生错误
};
//当Parent的子组件出现报错时候,会触发getDerivedStateFromError调用,并携带错误信息
static getDerivedStateFromError(error) {
console.log('@@@', error);
// 返回状态对象
return {
hasError: error };
}
componentDidCatch(error, info) {
// 统计页面的错误。发送请求发送到后台去
console.log(error, info);
}
render() {
return (
<div>
<h2>我是Parent组件</h2>
{
this.state.hasError ? <h2>当前网络不稳定,稍后再试</h2> : <Child />}
</div>
);
}
}
通信方式:
props
父子间通过props
children props
render props
消息订阅-发布
pubs-sub
等
集中式管理
redux
等
conText
生产者-消费者模式
组件之间的关系:
props
conText
欢迎在我的博客上访问:
https://lzxjack.top/