加载笔记内容...
加载笔记内容...
React Hooks 的出现不仅改变了组件的编写方式,更体现了 React 团队对函数式编程理念的深度实践。其核心思想是将 UI 视为状态的函数:UI = f(state)
。这种设计范式带来了三个关键特性:
useState
等 Hook 将状态管理从类组件的实例中解放useEffect
将传统生命周期中的副作用操作统一为声明式编程模型每个函数组件对应一个 Fiber 节点,其核心字段与 Hooks 相关:
1type Fiber = {
2 memoizedState: Hook | null, // Hooks 链表头节点
3 stateNode: Component, // 组件实例
4 updateQueue: UpdateQueue, // 更新队列
5 // ...其他字段省略
6};
Hooks 以链表形式存储在 Fiber 节点中,每个 Hook 节点的结构:
1type Hook = {
2 memoizedState: any, // 当前状态值
3 baseState: any, // 基准状态
4 baseQueue: Update<any> | null, // 待处理更新队列
5 queue: UpdateQueue<any> | null, // 更新队列
6 next: Hook | null, // 下一个 Hook
7};
React 采用双缓冲机制管理状态更新:
1function dispatchSetState(fiber, queue, action) {
2 const lane = requestUpdateLane(fiber);
3 const update = {
4 lane,
5 action,
6 hasEagerState: false,
7 eagerState: null,
8 next: null
9 };
10
11 // 将更新加入队列
12 const pending = queue.pending;
13 if (pending === null) {
14 update.next = update;
15 } else {
16 update.next = pending.next;
17 pending.next = update;
18 }
19 queue.pending = update;
20
21 // 调度更新
22 scheduleUpdateOnFiber(fiber, lane);
23}
1function updateState(initialState) {
2 const hook = updateWorkInProgressHook();
3 const queue = hook.queue;
4
5 // 获取待处理的更新队列
6 const pendingQueue = queue.pending;
7 if (pendingQueue !== null) {
8 const first = pendingQueue.next;
9 let newState = hook.baseState;
10 let update = first;
11
12 do {
13 const action = update.action;
14 newState = typeof action === 'function'
15 ? action(newState)
16 : action;
17 update = update.next;
18 } while (update !== first);
19
20 hook.memoizedState = newState;
21 hook.baseState = newState;
22 queue.lastRenderedState = newState;
23 }
24
25 return [hook.memoizedState, queue.dispatch];
26}
实现原理涉及三个关键阶段:
1function useEffect(create, deps) {
2 const hook = mountWorkInProgressHook();
3 const nextDeps = deps === undefined ? null : deps;
4
5 hook.memoizedState = pushEffect(
6 HookHasEffect | hookEffectTag,
7 create,
8 undefined,
9 nextDeps,
10 );
11}
典型问题场景:
1function Counter() {
2 const [count, setCount] = useState(0);
3
4 useEffect(() => {
5 const timer = setInterval(() => {
6 console.log(count); // 始终输出初始值
7 }, 1000);
8 return () => clearInterval(timer);
9 }, []);
10
11 return <button onClick={() => setCount(c => c+1)}>+</button>;
12}
解决方案:
1// 方案1:使用 ref 保持最新引用
2const countRef = useRef(count);
3countRef.current = count;
4
5// 方案2:添加依赖项 + 清理函数
6useEffect(() => {
7 const timer = setInterval(() => {
8 console.log(count);
9 }, 1000);
10 return () => clearInterval(timer);
11}, [count]);
1// 避免在每次渲染时执行复杂计算
2const [data] = useState(() => computeExpensiveValue());
当组件状态逻辑变得复杂时,推荐方案:
方案 | 适用场景 | 典型库 |
---|---|---|
Context + useReducer | 中小型应用 | 内置 |
Zustand | 轻量全局状态 | zustand |
Recoil | 原子化状态 | recoil |
1function useFetch(url) {
2 const [data, setData] = useState(null);
3 const [error, setError] = useState(null);
4
5 useEffect(() => {
6 fetch(url)
7 .then(res => res.json())
8 .then(setData)
9 .catch(setError);
10 }, [url]);
11
12 return { data, error };
13}
React 18 引入的并发渲染特性对 Hooks 的影响:
1function SearchResults() {
2 const [query, setQuery] = useState('');
3 const deferredQuery = useDeferredValue(query);
4
5 return (
6 <>
7 <input value={query} onChange={e => setQuery(e.target.value)} />
8 <Results query={deferredQuery} />
9 </>
10 );
11}
use
开头命名自定义 HookuseCallback
记忆事件处理函数useMemo
缓存昂贵计算1const memoizedValue = useMemo(() =>
2 computeExpensiveValue(a, b),
3 [a, b]
4);
5
6const handleClick = useCallback(() => {
7 // 处理点击事件
8}, [deps]);
部分开发者认为 React 应该自动捕获所有依赖项,但官方团队坚持显式声明依赖的策略,原因在于:
在 React 17 之后,两者性能差异已基本消除。但在以下场景仍需注意:
React Hooks 的设计体现了前端开发从面向生命周期编程到声明式状态管理的范式转变。深入理解其实现原理不仅能帮助开发者编写更健壮的代码,更能把握 React 未来的发展方向。随着并发模式的普及,Hooks 将在构建高性能、可维护的现代 Web 应用中发挥更重要的作用。