关于React中函数Function组件和类Class组件的相同点和区别点,我想大家都已经知晓了,比如:
1、相同点
- 无论是使用函数或是类来声明一个组件,它决不能修改它自己的 props
- 所有 React 组件都必须是纯函数,并禁止修改其自身 props
- React是单项数据流,父组件改变了属性,那么子组件视图会更新。、
- 父组件props有变化时,子组件随之而改变
- 属性 props 是外界传递过来的,状态 state 是组件本身的,状态可以在组件中任意修改
- 组件的属性和状态改变都会更新视图。
2、不同点
区别 | 函数组件 | 类组件 |
---|---|---|
生命周期 | 没有 | 有 |
this | 没有 | 有 |
state | 没有 | 有 |
改变state | Hook:useState | this.setState() |
性能 | 高(不用实例化) | 低(需要实例化) |
3、案例
(1)类class组件
不过,今天同事问了我一个简单的代码问题,之前也没认真想过,一下子没回答出来。
话不多说,看一下:
import React, { Component } from 'react'
class App extends Component {
state={
number:0
}
handleClick=()=>{
for(let i = 0 ;i<5;i++){
setTimeout(()=>{
this.setState({ number:this.state.number+1 })
console.log(this.state.number)
},1000)
}
}
render(){
return(
<div>
当前的number:{this.state.number}
<button onClick={this.handleClick}>点击我</button>
</div>
)
}
}
export default App
(2)函数组件
import React ,{ useState } from 'react';
const App = () => {
const [ num ,setNumber ] = useState(0)
const handleClick1 = ()=> {
for(let i=0; i<5;i++ ){
setTimeout(() => {
setNumber(num+1)
console.log(num)
}, 1000)
}
}
return (
<div>
当前的num值:{num}
<br/>
<button onClick={handleClick1}>改变num</button>
</div>
);
};
export default App;
(3)对比结果
可以看出:
第一个类class组件,打印结果是:1 2 3 4 5,页面上显示的是5;
第二个函数Function组件,打印结果是:0 0 0 0 0,页面上显示的是1
这个结果看上去很唬人,认真分析一下:
- 类class组件中,通过一个实例化的class,去维护组件中的各种状态。点击按钮,循环五次setTimeout,同时由于setState没有在react正常的函数里执行上下文,而是位于异步的setTimeout里面,所以
批量更新
的条件被破坏,可以同步拿到每次setState之后的值
(关于React批量更新可查看
: React学习笔记——this.setState的基础使用和不同传参方法详解) - 函数Function组件中,没有一个状态去保存这些信息,它
只是一个函数
,每一次函数上下文执行,所有变量,常量都重新声明
,执行完毕,再被垃圾机制回收。所以如上,无论setTimeout执行多少次,都是在当前函数上下文执行,此时num = 0不会变,之后setNumber执行,函数组件重新执行之后,num才变化。
结论:
- 所以, 对于class组件,我们
只需要实例化一次
,实例中保存了组件的state
等状态。对于每一次更新只需要调用render方法
就可以。 - 但是在function组件中,我们
每一次更新都是一次新的函数执行
,为了保存一些状态,执行一些副作用钩子,react-hooks应运而生,去帮助记录组件的状态,处理一些额外的副作用。
4、有趣拓展
对于函数Function的写法,在set的时候有函数型和对象型两种写法,我们观察一下这两种的区别:
import React ,{ useState } from 'react';
const App = () => {
const [ num ,setNumber ] = useState(0)
const [ val ,setVal ] = useState(0)
const handleClick1 = ()=> {
for(let i=0; i<5;i++ ){
setTimeout(() => {
setNumber(num+1)
console.log('num',num)
}, 1000)
}
}
const handleClick2 = () =>{
for(let i=0; i<5;i++ ){
setTimeout(() => {
setVal(val=>val+1)
console.log('val',val)
}, 1000)
}
}
return (
<div>
当前的num值:{num}
<br/>
<button onClick={handleClick1}>改变num</button>
<br/>
当前的val值:{val}
<br/>
<button onClick={handleClick2}>改变val</button>
</div>
);
};
export default App;
可以看出,当里面是函数式的写法时,虽然每次打印的val值是0,但是他会依赖于上一次的状态,所以val值每次都会+1;而对象式的写法却不会。