React 源碼解析 - Hooks 系列

本文深入探讨React Hooks的内部工作机制,以v16.13.1版本的源码为基础,解析useState和useEffect的实现。通过源码分析,揭示了调用Hooks时幕后发生的事情,包括Hook的数据结构、生命周期管理和状态更新等关键环节。文章详细介绍了useState在挂载和更新阶段的逻辑,以及useEffect的mountEffect和updateEffect的执行过程,帮助读者理解React Hooks的底层原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

這篇要來研究下每次寫 react 都在使用的 Hooks。說到 hooks,大家一定都用過,我自己也常用,但是一直都不知道 hook 背後到底是怎麼運作的,出現 bug 時就是百度解決辦法,半猜半試解決,其實都有點不明所以。所以,今天就要來把 react hooks 慢慢深入解析一下。

正文

調用 React Hook 到底背後發生了什麼?

前情提要下,這篇分析 hooks 所用的 React 源碼是 v16.13.1 的,因為最新版本的源碼關於 hooks 這塊個人能力不夠,不是看的很清楚,所以還是先以穩定版本來看,不過具體實現也都沒有大問題的。

初探 Hooks 源碼

當我們在調用 useXXX() 時到底背後發生了什麼?雖然看源碼真的有點累、挺難頂的,但是要想真正理解原理,還是看源碼最實在。

平時我們在項目中引入 hooks 時一般都是這樣寫的:

import React, {
    useState } from 'react';

後面的 from 'react' 其實就提示了我們 useState 這個 hook 源碼藏在 /react 這個目錄下,所以順藤摸瓜,最後就找到了導出所有 hooks 的地方:react/packages/react/src/React.js,跟 hooks 相關的源碼如下:

...

import {
   
  useCallback,
  useContext,
  useEffect,
  useImperativeHandle,
  useDebugValue,
  useLayoutEffect,
  useMemo,
  useReducer,
  useRef,
  useState,
  useResponder,
  useTransition,
  useDeferredValue,
} from './ReactHooks';

...
...

export {
   
  Children,
  createRef,
  Component,
  PureComponent,
  ...
  useCallback,
  useContext,
  useEffect,
  useImperativeHandle,
  useDebugValue,
  useLayoutEffect,
  useMemo,
  useReducer,
  useRef,
  useState,
  ...
};

這個文件中,我們看到其實所有 hooks 都來自 /ReactHooks.js,所以當然就再去看看啦~

Dispatcher

  • /ReactHooks.js 源碼如下:
/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow
 */

import type {
   Dispatcher} from 'react-reconciler/src/ReactInternalTypes';
import type {
   
  MutableSource,
  MutableSourceGetSnapshotFn,
  MutableSourceSubscribeFn,
  ReactContext,
} from 'shared/ReactTypes';
import type {
   OpaqueIDType} from 'react-reconciler/src/ReactFiberHostConfig';

import ReactCurrentDispatcher from './ReactCurrentDispatcher';

type BasicStateAction<S> = (S => S) | S;
type Dispatch<A> = A => void;

function resolveDispatcher() {
   
  const dispatcher = ReactCurrentDispatcher.current;
  if (__DEV__) {
   
    if (dispatcher === null) {
   
      console.error(
        'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' +
          ' one of the following reasons:\n' +
          '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
          '2. You might be breaking the Rules of Hooks\n' +
          '3. You might have more than one copy of React in the same app\n' +
          'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.',
      );
    }
  }
  // Will result in a null access error if accessed outside render phase. We
  // intentionally don't throw our own error because this is in a hot path.
  // Also helps ensure this is inlined.
  return ((dispatcher: any): Dispatcher);
}

export function getCacheForType<T>(resourceType: () => T): T {
   
  const dispatcher = resolveDispatcher();
  // $FlowFixMe This is unstable, thus optional
  return dispatcher.getCacheForType(resourceType);
}

export function useContext<T>(Context: ReactContext<T>): T {
   
  const dispatcher = resolveDispatcher();
  if (__DEV__) {
   
    // TODO: add a more generic warning for invalid values.
    if ((Context: any)._context !== undefined) {
   
      const realContext = (Context: any)._context;
      // Don't deduplicate because this legitimately causes bugs
      // and nobody should be using this in existing code.
      if (realContext.Consumer === Context) {
   
        console.error(
          'Calling useContext(Context.Consumer) is not supported, may cause bugs, and will be ' +
            'removed in a future major release. Did you mean to call useContext(Context) instead?',
        );
      } else if (realContext.Provider === Context) {
   
        console.error(
          'Calling useContext(Context.Provider) is not supported. ' +
            'Did you mean to call useContext(Context) instead?',
        );
      }
    }
  }
  return dispatcher.useContext(Context);
}

export function useState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
   
  const dispatcher = resolveDispatcher();
  return dispatcher.useState(initialState);
}

export function useReducer<S, I, A>(
  reducer: (S, A) => S,
  initialArg: I,
  init?: I => S,
): [S, Dispatch<A>] {
   
  const dispatcher = resolveDispatcher();
  return dispatcher.useReducer(reducer, initialArg, init);
}

export function useRef<T>(initialValue: T): {
   |current: T|} {
   
  const dispatcher = resolveDispatcher();
  return dispatcher.useRef(initialValue);
}

export function useEffect(
  create: () => (() => void) | void,
  deps: Array<mixed> | void | null,
): void {
   
  const dispatcher = resolveDispatcher();
  return dispatcher.useEffect(create, deps);
}

export function useLayoutEffect(
  create: () => (() => void) | void,
  deps: Array<mixed> | void | null,
): void {
   
  const dispatcher = resolveDispatcher();
  return dispatcher.useLayoutEffect(create, deps);
}

export function useCallback<T>(
  callback: T,
  deps: Array<mixed> | void | null,
): T {
   
  const dispatcher = resolveDispatcher();
  return dispatcher.useCallback(callback, deps);
}

export function useMemo<T>(
  create: () => T,
  deps: Array<mixed> | void | null,
): T {
   
  const dispatcher = resolveDispatcher();
  return dispatcher.useMemo(create, deps);
}

export function useImperativeHandle<T>(
  ref: {
   |current: T | null|} | ((inst: T | null) => mixed) | null | void,
  create: () => T,
  deps: Array<mixed> | void | null,
)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值