import React from ‘react’;
class Test extends React.Component {
componentDidUpdate() {
console.log(‘Test componentDidUpdate’);
}
render() {
return
;}
}
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState((state) => ({
count: state.count + 1,
}));
}
handleTestClick() {}
render() {
return (
);
}
}
这代码没什么好说的,每次点击click
更新state
,我现在问几个问题,你先思考一下~
-
每次点击
click
的时候,Test
组件会打印Test componentDidUpdate
吗? -
如果我把
Test
组件的React.Component
替换为React.PureComponent
,结果与上面一样吗?如果不一样,为什么? -
如果我修改这一行代码
<Test onClick={this.handleTestClick} />
为<Test onClick={() => {}} />
结果又如何?
shouldComponentUpdate
好像所有的内容都要从这个东西说起,shouldComponentUpdate
作为React
生命周期的一部分,大多数React
开发者至少还是听说过它的,简单来说在这个函数中返回一个布尔值,React
会根据这个布尔值来判断组件是否需要重新渲染。
shouldComponentUpdate
接收两个参数,一个是更新后的props
,一个是更新后的state
,可以通过比较两个props
和state
来决定是否需要重新渲染组件。
import React from ‘react’;
class Test extends React.Component {
componentDidUpdate() {
console.log(‘Test componentDidUpdate’);
}
// 每次点击 click 都会打印 Test componentDidUpdate
// 添加这个函数后当 count 没有变化时不会打印 Test componentDidUpdate
shouldComponentUpdate(nextProps) {
if (this.props.count === nextProps.count) {
return false;
}
return true;
}
render() {
return
;}
}
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState((state) => ({
count: state.count,
}));
}
render() {
return (
);
}
}
这段代码也算比较直观的说明了shouldComponentUpdate
的用法,为什么要这么做?当只有一个Test
组件的时候可能影响不大,那如果有一千个乃至一万个Test
的时候呢,每点击一次click
就有一千个、一万个Test
的componentDidUpdate
被调用,这就有点夸张了。所以当你在使用循环渲染组件的时候就一定要注意到这一个点,它可能会成为你应用的瓶颈。
现在我们来解一下第一个问题,每次点击click
的时候,Test
组件会打印Test componentDidUpdate
吗?
是的,每次点击click
的时候,Test
组件会打印Test componentDidUpdate
,除非我们在Test
中定义了shouldComponentUpdate
,同时返回了false
阻止其重新渲染。
PureComponent
关于React
的这个 API,相信大家也没有那么陌生,根据官方文档的说法Component
和PureComponent
很相似,两者的区别在于PureComponent
中实现了shouldComponentUpdate
函数,这也是为什么我说要从shouldComponentUpdate
说起。
import React from ‘react’;
class Test extends React.PureComponent {
componentDidUpdate() {
console.log(‘Test componentDidUpdate’);
}
// 错误的用法
shouldComponentUpdate(nextProps) {
if (this.props.count === nextProps.count) {
return false;
}
return true;
}
render() {
return
;}
}
如果你在PureComponent
中又使用了shouldComponentUpdate
你应该会得到这样一个警告,侧面也告诉我们PureComponent
已经实现了shouldComponentUpdate
这个函数了。
Test has a method called shouldComponentUpdate(). shouldComponentUpdate should not be used when extending React.PureComponent. Please extend React.Component if shouldComponentUpdate is used.
官网文档中说PureComponent
中以浅层对比props
和state
的方式来实现了这个函数,也就是浅比较,那什么又是浅比较呢?可以简单的理解为a === b
,这里面还是有一些说头的,不过不在本文探讨范围内,举两个例子,大家可以自行搜索理解。
let a = 5;
let b = 5;
let c = {};
let d = {};
console.log(a === b); // true
console.log(c === d); // false
在来看一段因为不当的代码导致的问题,大家一定要注意这部分的内容。
import React from ‘react’;
class Test extends React.PureComponent {
// 根据从 App 中传来的 animal 渲染组件
// 在 App 中每次点击添加新的动物后, 这里还是原来的 dog
render() {
return
}
}
export default class App extends React.Component {
constructor(props) {
super(props);
// 默认为一只狗
this.state = { animal: [‘dog’] };
this.handleClick = this.handleClick.bind(this);
}
// 每次点击把新的值添加进 animal 中
// 此处有一个 Bug, 由于 animal.push 方法虽然更新了原来的数组
// 但是他们还是一个数组(这个说法有些奇怪), 指针还是一样的
// 可能需要读者自行搜索理解 JS 中基本类型和引用类型的存储方式
// 所以当 Test 组件接收到新的 animal 时, 通过浅比较会发现它们其实是一样的
// 也就意味着 Test 不会重新渲染
handleClick(val) {
const { animal } = this.state;
animal.push(val)
this.setState({
animal,
});
}
// 根据 state 中的 animal 渲染组件
render() {
return (
);
}
}
看到这里相信你应该能解答第二个问题和第三个问题了,不过我们还是一起再来看看~
问:如果我把Test
组件的React.Component
替换为React.PureComponent
,结果与上面一样吗?如果不一样,为什么?
答:因为每次传递props
中的onClick
都是App
组件中的handleTestClick
,同时使用了PureComponent
,所以每次浅比较都是一致的,所以不会在打印Test componentDidUpdate
了。
问:如果我修改这一行代码<Test onClick={this.handleTestClick} />
为<Test onClick={() => {}} />
结果又如何?
答:虽然使用了PureComponent
,但是由于App
每次调用render
函数的时候都会重新声明一个方法,此方法和上一次传递给Test
的方法不同,所以每次点击还是会打印Test componentDidUpdate
。
剩点内容补充
除了上述两个 API 以外,其他 API 或多或少只是它们的改版,所以我就放在一起说了。
memo
React.memo
在我看来就是PureComponent
无状态组件版本,如果用的是class
就用PureComponent
,如果用的是无状态组件就用memo
。
import React from ‘react’;
export default React.memo(function Test() {
return
});
// 它也可以接收第二个参数, 类似 shouldComponentUpdate
// 两个参数上次的props, 和当前的props
// 不传默认情况它们两个做浅比较, 传了由你自己控制
注意:此方法返回值与shouldComponentUpdate
相反,返回值为true
时不重新渲染组件。
useCallback 和 useMemo
这两个 API 是React Hook
的一部分,为什么要放在一起说呢?因为它们非常的类似,据官方文档useCallback(fn, deps)
相当于useMemo(() => fn, deps)
。
因为React Hook
,我现在基本很少写class
组件了,原因是什么相信用过的小伙伴都清楚,本文不阐述这方面的内容,只想再问你一个问题:handleClick
方法每次都会重新定义吗?
import React from ‘react’;
export default function Test() {
const handleClick = () => console.log(‘click’);
return
}
答案是会的,不信你可以验证一下。
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。**
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-Gznu9JHp-1715824241488)]
[外链图片转存中…(img-00BXywbv-1715824241489)]
[外链图片转存中…(img-VemCaBv7-1715824241489)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!