2024-04-01 16:00:54
React Hooks 核心原理及源码分析
React Hooks 自 React 16.8 引入以来,彻底改变了函数组件的开发方式,使其具备了与类组件等价的功能。本教程将从 Hooks 的核心原理入手,逐步解析每个常用 Hook 的实现细节,并结合 React 18 的源码(react 和 react-reconciler)进行分析。通过本文,你将彻底理解 Hooks 的工作机制及其在 Fiber 架构中的实现。
Hooks 核心原理什么是 Hooks?Hooks 是 React 提供的一组函数,允许函数组件管理状态、处理副作用、访问上下文等功能。它解决了类组件的几个痛点:
示例:类组件 vs 函数组件
类组件:
class Counter extends React.Component { state = { count: 0 }; componentDidMount() { document.title = `Count: ${this.state.count}`; } componentDidUpdate() { document.title = `Count: ${this.state.count}`; } render() { return ( <button onClick={() => this.setState({ count: this.state.count + 1 })}> {this.state.count} </button> ); }}使用 Hooks:
function Counter() { const [count, setCount] = useState(0); useEffect(() => { document.title = `Count: ${count}`; }, [count]); return <button onClick={() => setCount(count + 1)}>{count}</button>;}Hooks 的实现基础:Fiber 架构Hooks 依赖于 React 的 Fiber 架构。每个函数组件对应一个 Fiber 节点,Hooks 的状态和副作用通过 Fiber 的 memoizedState 属性存储和管理。
Fiber 节点结构
const fiber = { tag: FunctionComponent, // 函数组件类型 type: Counter, // 组件函数 memoizedState: null, // Hooks 状态链表 updateQueue: null, // 更新队列 child: null, // 子 Fiber sibling: null, // 兄弟 Fiber return: null // 父 Fiber};Hooks 的调用规则
示例:错误用法
function BadComponent() { if (Math.random() > 0.5) { useState(0); // 错误:条件调用导致顺序不固定 } const [value] = useState(1); return <div>{value}</div>;}Hooks 的工作流程React 通过一个全局变量 currentDispatcher 管理 Hooks 的实现:
// react/src/ReactCurrentDispatcher.jsconst ReactCurrentDispatcher = { current: null};在函数组件渲染时,current 指向 HooksDispatcherOnMount(挂载)或 HooksDispatcherOnUpdate(更新)。每个 Hook 函数(如 useState)从 current 获取具体实现。
Hooks 状态链表Hooks 状态以单向链表形式存储在 Fiber 的 memoizedState 中,每个节点对应一个 Hook 调用:
const hook = { memoizedState: null, // Hook 的当前值(如 useState 的 state) queue: null, // 更新队列 next: null // 下一个 Hook};示例:多个 Hooks
function Component() { const [a] = useState(1); const [b] = useState(2); return <div>{a + b}</div>;}Fiber 的 memoizedState:
hook1: { memoizedState: 1, queue: {...}, next: hook2 }hook2: { memoizedState: 2, queue: {...}, next: null }渲染阶段的 Hooks 执行在 Fiber 的 render 阶段,React 调用函数组件,依次执行每个 Hook,并维护一个全局指针 currentlyRenderingFiber 和 workInProgressHook。
常用 Hooks 源码解读useState 源码解读作用:useState 为函数组件提供状态管理,返回状态值和更新函数。
示例
const [count, setCount] = useState(0);源码解析useState 定义在 react/src/ReactHooks.js:
function useState(initialState) { const dispatcher = ReactCurrentDispatcher.current; return dispatcher.useState(initialState);}挂载时:mountState
更新时:updateState
作用:useEffect 用于处理副作用,如数据获取、订阅等。
示例
useEffect(() => { document.title = `Count: ${count}`;}, [count]);源码解析useEffect 定义在 react/src/ReactHooks.js:
function useEffect(create, deps) { const dispatcher = ReactCurrentDispatcher.current; return dispatcher.useEffect(create, deps);}挂载时:mountEffect
更新时:updateEffect
作用:useContext 允许函数组件访问上下文。
示例
const theme = useContext(ThemeContext);源码解析useContext 定义在 react/src/ReactHooks.js:
function useContext(Context) { const dispatcher = ReactCurrentDispatcher.current; return dispatcher.useContext(Context);}实现细节
React Hooks 通过 Fiber 架构和链表结构实现了状态和副作用的管理。其核心在于:
通过深入理解 Hooks 的实现原理,可以更好地使用它们并避免常见问题。