本博客 hjy-xh,转载请申明出处
问题列表
带着以下问题学习:
- Hook解决了什么问题?
- Hook有哪些优势?
- 为什么有Hook?
- useState方括号有什么用?
- 为什么每次更新的时候都要运行 Effect?
- useMemo 和 shouldComponentUpdate 有什么区别?
Hook概述
React 16.8的新增特性
它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性(是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数)
Hook的动机
- 在无需修改组件结构的情况下复用状态逻辑
在组件之间复用状态逻辑很难
Hook出现之前,将可复用性行为”附加“到组件的解决方案有render props
和高阶组件
,但是这类方案需要重新组织组件结构,可能会很麻烦,进而让代码难以理解。
- Hook将组件中相互关联的部分拆分成更小的函数(比如监听事件、请求数据),而并非强制按照生命周期划分
组件期初很简单,但是逐渐会被状态逻辑和副作用充斥,相关关联且需要对照修改的代码被拆分(监听事件),不相关的代码在同一个方法中(componentDidMount
、componentWillUnmount
)组合在一起,容易产生bug。
- 降低学习门槛
对class的学习(需要理解JS中的this工作方式)
Hook使用规则
只能在函数最外层调用Hook。不要在循环、条件判断或者子函数中调用
这样能够确保Hook在每一次渲染中都按照同样的顺序被调用
只能在React的函数组件中调用Hook(包括自定义的Hook)
常见的Hook
基础 Hook
- useState
- useEffect
- 说明
- 可以把该Hook看做是`componentDidMount`、`componentDidUpdate`、`componentWillUnmout`三个函数的组合 - React保证了每次运行effect的同时,DOM都已经更新完毕 - 与`componentDidMount`或`componentDidUpdate`不同,使用useEffect调度的effect不会阻塞浏览器更新屏幕,这让应用看起来响应更快 - effect中可选的清除机制在组件卸载的时候触发
- 使用技巧
- 使用多个 Effect 实现关注点分离(按照代码的用途分离它们),React将按照effect声明的顺序一次调用组建的每一个effect - 跳过 Effect 进行性能优化(第二个参数)
- 说明
- useContext
接收一个 context 对象(`React.createContext` 的返回值)并返回该 context 的当前值
额外的 Hook
- useReducer
useState 的替代方案。它接收一个形如(state, action) => newState
的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法 - useCallback
useCallback(fn, deps) 相当于 useMemo(() => fn, deps) - useMemo
可以把 useMemo 作为性能优化的手段,但不要把它当成语义上的保证
可以使用它缓存一些相对耗时的计算,也非常适合用于存储引用类型的数据,可以传入对象字面量,匿名函数等,甚至是 React Element - useRef
useRef 返回一个可变的 ref 对象,其.current
属性被初始化为传入的参数(initialValue) - useImperativeHandle
- useImperativeHandle 可以在使用 ref 时自定义暴露给父组件的实例值
- 在大多数情况下,应当避免使用 ref 这样的命令式代码
- useImperativeHandle 应当与
forwardRef
一起使用
- useLayoutEffect
其函数签名与 useEffect 相同,但它会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染 - useDebugValue
useDebugValue 可用于在 React 开发者工具中显示自定义 hook 的标签,它接受一个格式化函数作为可选的第二个参数。该函数只有在 Hook 被检查时才会被调用。它接受 debug 值作为参数,并且会返回一个格式化的显示值
- useReducer
自定义Hook
通过自定义 Hook,可以将组件逻辑提取到可重用的函数中
字应该始终以 use 开头,这样可以一眼看出其符合 Hook 的规则
自定义 Hook 是一种自然遵循 Hook 设计的约定,而并不是 React 的特性
原理
函数组件执行函数
renderWithHooks
- 作用
它是调用function组件
函数的主要函数,从源码中看,它首先会置空即将调和渲染的workInProgress树
的memoizedState
和updateQueue
,把新的hooks信息挂载到这两个属性上,然后在组件commit阶段
,将workInProgress树
替换成current树
,替换真实的DOM元素节点。并在current树保存hooks信息。 - 步骤
- 执行函数组件
- 改变
ReactCurrentDispatcher
对象
- 作用
初始化hooks
相关hook实际执行的函数:
1
2
3
4
5
6
7useState: mountState, // 初始化useState
useEffect: mountEffect, // 初始化useEffect
useLayoutEffect: mountLayoutEffect, // 初始化useLayoutEffect
useMemo: mountMemo, // 初始化useMemo
useReducer: mountReducer, // 初始化useReducer
useRef: mountRef, // 初始化useRef
useCallback: mountCallback, // 初始化useCallbackmountWorkInProgressHook
生成hook链表- 在一个函数组件第一次渲染时,每个hook执行,都会产生一个hook对象,并形成链表结构,绑定在
workInProgress
的memoizedState
属性上 - hook上的状态,绑定在当前hook对象的
memoizedState
属性上 - 对于effect副作用钩子,会绑定在
workInProgress.updateQueue
上,等到commit阶段
,dom树构建完成,再执行每个 effect 副作用钩子。
- 在一个函数组件第一次渲染时,每个hook执行,都会产生一个hook对象,并形成链表结构,绑定在
更新hooks
相关hook实际执行的函数:
1
2
3
4
5
6
7useState: updateState, // 得到最新的state
useEffect: updateEffect, // 更新updateQueue
useLayoutEffect: updateLayoutEffect,
useMemo: updateMemo,
useReducer: updateReducer,
useRef: updateRef, // 获取ref对象
useCallback: updateCallbackupdateWorkInProgressHook
更新hook链表,找到对应的hooks