组件封装是React的核心思想,ReactJS有两种封装组件的方式:
- 以类(class)的方式封装组件
- 以函数(function)的方式封装组件
在旧的React官方文档中,是使用类组件的:
class HelloMessage extends React.Component {
render() {
return <div>Hello {this.props.name}</div>;
}
}
但到了新官方文档,已经改成使用函数组件了:
function HelloMessage({name}) {
return(
<div>Hello {name}</div>
)
}
那么,为什么会有两种组件封装方式呢?两者又有什么不同,我们应该如何选择?
React为什么会有两种组件封装方式?
从官方文档的变化就能看出,两种封装方式是有先后关系的,早期主要采用类的方式封装组件。主要原因是组件中的状态管理问题。
有时在组件中需要维护变量,比如统计某个按钮的点击次数,这种在内部需要维护变量的组件称为带状态组件(stateful components)。
在2019年2月6号之前,带状态组件只能以类的方式封装。早期官方文档给出了带状态组件的实例:
class Timer extends React.Component {
constructor(props) {
super(props);
this.state = { seconds: 0 };
}
tick() {
this.setState(state => ({
seconds: state.seconds + 1
}));
}
componentDidMount() {
this.interval = setInterval(() => this.tick(), 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
return (
<div>
Seconds: {this.state.seconds}
</div>
);
}
}
root.render(<Timer />);
这也是早期React以类组件为主的原因,毕竟大部分组件都是带状态的。
直到2019年2月,情况发生了改变。React 16.8.0发布,该版本引入了Hooks的概念
Hooks — a way to use state and other React features without writing a class.
有了Hooks,在函数组件中也可以实现状态管理了,使用方法如下:
import { useState } from 'react';
export default function MyApp() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (<button onClick={handleClick}>{count}</button>);
}
并且Hooks只能在函数组件中使用。
两种封装方式的优缺点
有了hooks,两种组件封装方式好像又站在了同一起跑线上。那么在其他方面两者有什么优缺点呢?
从官方文档改为使用函数组件可以推测,函数组件成了新的趋势,那么是不是函数组件有更多优点呢?
个人觉得,函数组件的优点是使用起来更加简单,主要体现在,与类组件相比:
- 函数组件不需要构造函数,类组件如果需要管理状态,就必须有构造函数
- 函数组件不需要
render()
函数,类组件必须通过render()
函数返回JSX
当然,函数组件也有它的问题,组件生命周期的管理就是问题之一。
类组件可以利用生命周期函数,在不同的生命周期执行不同代码。比如像上面代码中,在组件被挂载时,也就是componentDidMount()
时创建计时器,并在组件被移除,也就是componentWillUnmount()
删除计时器。
生命周期的问题,在函数组件中也是可以解决的,官方文档中就给出了方式,就是利用useEffect。
应该选择哪种组件封装方式?
Hooks出现以后,函数组件逐渐受到追捧,相应的工具也越来越完善。
useRef
、useNavigate
、useLocation
等官方hook的出现,以及Redux等常用的项目纷纷增加了对hooks的支持,这也使得函数组件使用起来越来越方便,从而形成良性循环,函数组件使用越来越广泛。
所以总的来说,更建议采用函数的方式封装组件,这应该也是官方鼓励的趋势。