目前正在学习深入浅出React【真正吃透React知识链路与底层逻辑】共23讲_哔哩哔哩_bilibili
React-hooks的设计动机,使用方法和深层原理
目录
问题集
- hooks所引起的变化并没有访问任何生命周期方法?它是一套完全不同的体系?
- 为什么class component的this有时候得不到
- useState为什么底层是链表而不是数组?
- 猜测是为了更好地unmount组件
- 对于其他hooks的源码认识
设计动机
类组件 VS 函数组件
- 类组件需要继承Component,函数组件是定义函数,不能够继承
- 类组件能够访问生命周期方法,但是函数组件不行
- 类组件的操作大量基于this,函数组件中没有this
- 类组件可以定义和维护state,但是在hooks之前的函数组件没法处理state
类组件的缺点
- 太过于笨重,难以上手
- render的状态不一定会与data保持一致,因为this有时候会改变
- 常见于setTimeout,在timeout的时间内,数据有可能发生了变化,导致render的结果是基于改变了的数据,而不是调用setTimeout的那一刻
函数组件的缺点
- 没办法处理state,还有生命周期
Hooks解决的问题
- 为函数组件引入state和生命周期
Hooks的问题
- 无法替代getSnapshotBeforeUpdate / componentDidCache 等等生命周期
- 太过于轻量级,有时候没法处理更复杂的业务逻辑
- 有严格的代码逻辑要求
为什么要用hooks
- 告别难用的Class Component
- this会有奇怪的表现如下段代码,当我点击按钮的时候程序会报错, 但是如果我将函数的调用额外加一层调用链路就不会报错
export default class App extends Component { state = { name: "qwe", age: "99", }; changeAge() { this.setState({ age: "100", }); } render() { return ( // <button onClick={() => this.changeAge()}> 这样不会报错 <button onClick={this.changeAge}> {/* 这样会报错 */} {this.state.name} age is {this.state.age} </button> ); } }
- 生命周期函数
- this会有奇怪的表现如下段代码,当我点击按钮的时候程序会报错, 但是如果我将函数的调用额外加一层调用链路就不会报错
- 拆分componentDidMount/didUpdate/willUnmount里面的业务逻辑进useEffect,让逻辑更相关的贴合更近
- 更简单重用含有state的逻辑,我们可以将含有state还有一些hooks的逻辑全都单独封装成一个函数,然后哪里需要就调用
- 函数组件的编程思想更贴近React的设计理念,也就是UI=f(data)
使用方法
全面的hook API可以进入官方文档,Hook API 索引 – React
useState
const App = ()=>{
// initialize state a as 1, and get setA which can manipulate a
const [a, setA] = useState(1);
return <button onClick={()=>setA(a + 1)} />
}
- 引入了state和修改state的方法
- 返回一个有两个元素的数组,第一个元素为状态变量,第二个元素为设置这个状态并驱动试图更新的API
- 一般的命名规则为:[a, setA] = useState(intialValue)
useEffet
const App = ({a,b})=>{
const [c, ] = useState(123);
useEffect(()=>{
// 这个useEffect没有第二个参数(deps数组),这个effect会在每一次函数组件被调用的时候都运行
})
useEffect(()=>{
// 这个useEffect的deps数组是一个空数组,意味着每一次检查deps数组他都不会变化,这个effect只会在这个component第一次被调用的时候运行
// 一般用例为fetch数据,在全局事件中订阅
}, [])
useEffect(()=>{
return ()=>{
// useEffect的返回值是一个在这个组件将要被unMount的时候运行的函数
// 一般可以用来取消订阅
// 不论deps数组是什么,返回的clean up函数都只会跑一次
}
}, anything)
useEffect(()=>{
// 这个effect会在a,b,c之中任何一个有变化的时候被调用(包扩第一次)
},[a,b,c])
}
- 引入了生命周期
- 有两个参数
- 第一个参数是需要执行的sideEffect,函数自身提供mounting和updating的effect,函数返回的另外一个函数提供unmounting的effect
- 第二个参数是deps数组,这个参数决定了什么时候effect函数会被执行,当有任意的deps数组的元素发生了变化,(对比通过Object.is来完成,可以理解成对于地址的对比)。
useState深层原理
- 这部分需要深入挖掘,但是目前对fiber架构没有足够的认知,留到未来
- 通过useState的深层原理做研究,对于别的hooks可以做借鉴,但不一定完全一样,未来可以深究
- useState
- 第一次渲染,调用mountState,构建一个state的链表,并渲染
- 之后的渲染,调用updateSate,依次便利构建好的链表并渲染
- 如果useState出现在if或者switch里面,这意味着不是链表不会被便利,很可能我们得到的state错位了