目录
- React Hooks 介绍
- React Hooks 钩子函数的介绍和使用
- 模拟 React Hooks 钩子函数的实现原理
React Hooks
Reat Hooks 介绍
React Hooks 是 React 16.8 版本新添加的特性,实际上是一堆钩子函数。
React Hooks 主要是增强函数型组件的功能,让函数型组件可以实现类组件相同的功能,例如:
- 使用和存储 state(状态)
- 拥有处理副作用的能力
从 React 16.8 版本开始,React 官方推荐开发者使用函数去创建组件。
没有计划从 React 中移除 class,类组件依然可用。
副作用
在一个组件中,只要不是把数据转换成视图的代码,就属于“副作用”,或简称“作用”。例如:
- 获取 DOM 元素,为 DOM 元素添加事件
- 设置定时器
- 发送 ajax 请求
在类组件中,通常使用生命周期函数来处理这些副作用。
在函数型组件中,就要使用 Hooks 来处理副作用。
类组件的不足(Hooks 要解决的问题)
1. 缺少逻辑复用机制
通常情况下,开发者可能会使用 Render Props 或 高阶函数来实现逻辑复用。
这样实现的代码本身是非常复杂的。
通常是在原有组件的外面又包裹了一层组件,这一层组件又没有实际的渲染效果,增加了组件层次,显得十分臃肿。
组件嵌套层级的增加,增加了调试的难度,以及降低了运行效率。
2. 业务逻辑经常会变得很复杂且难以维护
这体现在类组件的生命周期函数中。
- 将一组相关的业务逻辑拆分到多个生命周期函数中。
- 在一个生命周期函数中存在多个不相干的业务逻辑。
例如:
- 在组件挂载时设置事件监听,又要在组件被卸载前清除事件监听
- 在组件挂载时,获取数据,同时进行其它初始化设置
- 在组件更新时,对比组件状态中的某个数据
3. 类成员方法不能保证 this 指向的正确性
当给一个元素绑定事件,在事件处理函数中更改状态的时候,通常要更正这个函数中的 this 指向,否则就会指向全局(window),严格模式下则会指向 undefined。
import React from 'react'
class Demo extends React.Component {
handleClick() {
console.log(this);
// 非严格模式 window
// 严格模式 this
}
render() {
return <button onClick={this.handleClick}>Click</button>
}
}
export default Demo
这需要开发者理解 JavaScript 中 this 的工作原理。
简单来说,函数中的 this 指向函数的调用者。
当前 click 事件直接绑定了类成员 handleClick 这个函数的引用,在触发时,它的调用者就是全局对象 window,在严格模式下就是 undefined。
开发者通常会使用以下3种方式去更改 this 指向:
- 使用箭头函数定义类成员
如果使用箭头函数定义这个成员,那函数内部的 this 就是定义时 this 的指向,即组件实例对象:
handleClick = () => {
console.log(this);
// this 指向组件实例对象
}
- 使用 bind 更改 this 指向
constructor() {
this.handleClick = this.handleClick.bind(this)
}
或者
render() {
return <button onClick={this.handleClick.bind(this)}>Click</button>
}
- 箭头函数嵌套(原理同方式1)
render() {
return <button onClick={() => this.handleClick()}>Click</button>
}
这些方式都使代码变得复杂难以理解和维护。
React Hooks 使用
Hook 意为钩子,React Hooks 就是一堆钩子函数,意思是“钩入” React 特性。
React 通过这些钩子函数对函数型组件进行增强,不同的钩子函数提供了不同的功能:
- 基础 Hook
- useState:用于为函数组件引入状态
- useEffect:让函数型组件拥有处理副作用的能力,类似生命周期函数
- useContext:在使用 createContext 跨组件层级传递数据时,简化获取数据的代码
- 额外的 Hook
- useReducer:另一种让函数组件引入状态的方式
- useCallback:性能优化 - 缓存函数,使组件重新渲染时得到相同的函数实例
- useMemo:性能优化 - 监测某个数据的变化,根据变化值计算新值,类似 Vue 中的计算属性
- useRef:获取 DOM 元素对象、保存跨组件周期的数据
- useImperativeHandle
- useLayoutEffect
- useDebugValue
钩子函数以 use
开头,自定义 Hook 也约定以 use
开头的命名规则,以方便 linter 插件校验。