目录
1. Hook
hook是可以给函数组件中添加了state和副作用的机制,可以不编写class组件使用react提供的特性。
上一个简单的计算器例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import React, { useState } from 'react'; function App() { const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } |
在函数组件内使用了useState
为函数组件引入了state
,按钮点击后state更新,组件重新渲染最新的state。这个useState
就是官方提供的hook,为了实现class组件中的功能,官方还提供了useEffect
,useContext
等。
2. 对比class
举一个当前很常见的组件:
- 进入当前组件(页面)开始请求api;
- 处理数据:统计、过滤或者重新聚合;
- 副作用:更新图表之类的;
- 事件监听与取消;
- 根据props更新组件。
2.1 componentDidMount
在组件挂载完成后执行一次。
class组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
class Example extends Component { constructor(props) { super(props); this.state = { originData = []; } } componentDidMount() { API().then(res => { this.setState({ originData: res }); }) } render() { return ( //... ) } } |
Hook:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
function Example(props) { const [originData, setOriginData] = useState([]); useEffect(() => { API().then(res => { setOriginData(res); }); }, []); return ( // ... ) } |
在class组件中componentDidMount
很清楚的标注了这个函数中的代码将在组件挂载完成后执行一次;函数组件中实现这样的功能是给useEffect
加上第二个参数,一个空数组来实现。
hook组件代码看着像是更简洁了一些,但个人认为class组件语意性更强。
2.2 对接口数据处理
类似于上面的例子,总之是要在请求成功后才可以操作。
2.3 执行副作用
class组件:
1 2 3 4 5 6 7 8 9 10 11 |
componentDidMount() { this.chart = createChart(); this.chart.init(); } componentDidUpdate(prevProps, prevState) { if(prevProps.active !== this.props.active) { this.chart.update(); } } |
Hook:
1 2 3 4 5 6 7 8 9 10 11 |
const chartRef = useRef(null); useEffect(() => { chartRef.current = createChart(); chartRef.current.init(); }, []) useEffect(() => { chartRef.current.update(); }, [props.active]); |
图表往往是依赖于一个实际的dom节点并在其上渲染的,因此初始化和更新必不可少。这个例子中,class组件实例会一直保存创建好的chart对象,可以很方便的在生命周期中使用;函数组件中如果用const
变量保存,很明显每次组件更新都是一个新的chart对象,想要创建一个组件实例需要引入useRef
hook,也许有更好的方式实现,但就这里来看还是支持class组件。
2.4 事件监听与取消
目前遇到的事件监听无非是监听窗口的resize、键盘事件这两种。
class组件:
1 2 3 4 5 6 7 8 9 10 |
handleResize = () => { // ... } componentDidMount() { window.addEventListener('resize', this.handleResize); } componentWillUnmount() { window.removeEventListener('resize', this.handleResize); } |
Hook:
1 2 3 4 5 6 7 8 9 10 |
useEffect(() => { const handleResize = () => { // ... } window.addEventListener('resize', this.handleResize); return () => { window.RemoveEventListener('resize', this.handleResize); } }, []); |
为了不影响其他组件,在本组件监听全局性的事件后,在组件销毁的时候应该取消监听。这个例子中hook的优势要明显一点,同一个事件handle的监听和取消在代码组织形式上放在了一起,看起来更紧凑。
2.5 prop更新
Class:
1 2 3 4 5 6 |
componetDidUpdate(prevProps) { if (prevProps.data !== this.props.data) { // 一些相关的更新操作 } } |
hook:
1 2 3 4 |
useEffect(() => { // 一些相关的更新操作 }, [props.data]); |
如果存在多个props属性更新需要执行一些副作用时,使用hook就无需写一长串比较运算。
3. 总结
根据上面的常用场景来看,目前使用class就能完成的事情,就没有必要用hook来代替了。当然官方也无时无刻在提醒,除非有必要(修复bug等),再完全用hook重写,否则保持已有的class组件就好。对比一定是有失偏颇的,像文档提到到抽出公共状态写一个自定义hook就比较好,上面的例子中并没有这样需求,所以并未提及。
这种新特性是渐进式的,相信当我有一天必须要用到它的时候,自然会用到。