前言
在提及到react hook
的时候,一般是跟函数组件结合在一起的,即react hook
只存在于函数组件中,在类组件中不受用,这是为什么?react hook
的出现是解决了什么?
函数组件和类组件
在聊这些之前,先来认识认识一下函数组件和类组件。类组件是有状态组件,而函数组件是无状态组件。
// 类组件
class App extend React.component{
constructor(props){
super(props);
this.state = {
text: '我是一个组件'
}
}
componentDidMount(){}
render(){
return (
<div>我是一个类组件</div>
)
}
}
export defalut App;
// 函数组件
function App(props){
return (
<div>我是一个函数组件</div>
)
}
由上面我们不难发现,两者的区别:
- 类组件需要继承
class
,函数组件不需要; - 类组件可以访问生命周期方法,函数组件不行;
- 类组件中可以获取实例化化后的
this
,并基于这个this
做各种事情; - 类组件可以定义并维护
state
,而函数组件不行。
React
框架的设计理念是UI = render(data)
,显然函数组件更加契合这个理念。虽然类组件继承了React.Component
,比函数组件多了很多功能,如state
和生命周期,但是大而全”的背后,是不可忽视的学习成本,而函数组件轻量、灵活,学习成本也很低。就拿上面的栗子来说,其实只是想在页面上显示一行文字,类组件的代码量明显高于函数组件的代码量。
React Hook的设计初衷
虽然函数组件更符合React的设计理念,但是由于无状态、无生命周期,导致了它在某些地方并不实用,于是React团队开始引进了React Hook
,补齐了这方面的“缺失”。
接下来介绍几个常用的Hook
方法:
useState
const [text, setText] = useState('初始化文本');
useState
的出现,便于了函数组件也可以使用**状态(state)**了。
useEffect
useEffect
的出现,可以解决生命周期的问题。过去我们习惯放在componentWillMount、componentDidUpdate和componentWillUnmount
三个生命周期里来做的事情可以放在useEffect
中来做。
// 每次渲染都会被执行
useEffect(() => {})
// 仅在挂载阶段执行一次
useEffect(() => {}, [])
// 在useEffect中返回一个函数,该函数会在组件被卸载的时候执行
// 卸载函数的执行并不会受第二个参数或者其他因素的影响
// 仅在挂载和卸载的时候执行
useEffect(() => {
return () => {}
},[])
// 当[]中的数据发生改变时,就会被执行
useEffect(() => {}, [])
更多react hook
可以翻阅官方文档,或者我之前写的 React hook
React Hook
让我们在函数组件的开发中更加方便了,没有了难以理解的class
,不用去思考如何解决this
问题,还解决了监听在componentDidMount
、取消监听在componentWillUnmount
这种业务逻辑难以拆分的问题。
Reat Hook的局限性
凡是有利就有弊,官方也没有说React Hook
的出现可以让函数组件完全替代类组件,原因有几个:
- 现有的
hook api
并不能完全补齐类组件的能力,比如getSnapshotBeforeUpdate
; - 由于函数组件的轻量性,将不能很好地将复杂组件拆分出来;
React Hook
在使用层面还有严格地规则约束,只能在函数组件中使用,且不能在循环、条件和嵌套函数中使用。
React Hook的工作原理
不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层以及任何 return 之前调用他们。遵守这条规则,你就能确保 Hook 在每一次渲染中都按照同样的顺序被调用。这让 React 能够在多次的
useState
和useEffect
调用之间保持 hook 状态的正确。
举个栗子:
import { useState } from 'react'
let isMounted = false;
function App(){
let name, setName, age, setAge: (arg0: number) => void
console.log('isMounted', isMounted);
if(!isMounted){
[name, setName] = useState('sugarMei');
isMounted = true;
}
[age, setAge] = useState(99);
const handleClick = () => {
setAge(100);
}
return (
<>
<p>姓名:{name}</p>
<p>年龄:{age}</p>
<button onClick={handleClick}>点击修改年龄</button>
</>
)
}
export default App
这时候就会报错了。React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render
React Hook
源码中,在组件首次挂载时,由于isMount = false
, 会将useState
生成两个hook
对象,并按照顺序形成一个hook
链,即链表;当页面更新时,由于isMount = false
,所以跳过了setName
,即相当于源码中遍历链表,结果直接跳过了某个节点,那我还如何寻找下一个节点?所以就报错了。
所以关于react hook
需要知道以下几点:
react hook
使用的是链表结构;hook
的顺序决定了自身在链表中的位置;- 组件每次渲染的时候,会随着链表读取
hook
对象。
总结
- 类组件比函数组件拥有更多的功能,如
state
、声明周期等,但是由于功能越多,学习的成本越多,也容易被误操作; - 函数组件轻而小,符合
React
的设计理念,结合Raect hook
可以解决state
和生命周期等问题; - 函数组件还无法完全取代类组件;
react hook
只能在函数组件中使用,且无法在循环,条件或嵌套函数中调用。
参考
如有错误,欢迎指出,感谢~