Skip to content

React

React是一个用于构建用户界面的JavaScript库。

react常用的hooks

React Hooks是React 16.8版本引入的新特性,它允许我们在不编写类组件的情况下使用状态和其他React特性。下面是一些React中常用的hooks的TSX实现:

  1. useState
    用于管理组件状态。
tsx
import React, { useState } from 'react';

function Counter() {
  // 声明一个名为count的状态变量,初始值为0
  const [count, setCount] = useState<number>(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
  1. useEffect
    用于处理副作用,如数据获取、订阅或手动修改DOM。
tsx
import React, { useState, useEffect } from 'react';
function Example() {
  const [count, setCount] = useState<number>(0);
  // 相当于componentDidMount和componentDidUpdate
  useEffect(() => {
    // 更新文档标题
    document.title = `You clicked ${count} times`;
    // 清理函数,相当于componentWillUnmount;在依赖项count改变时副作用函数执行前调用
    return () => {
      console.log('Component will unmount');
    };
  }, [count]); // 仅在count改变时执行

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
  1. useContext
    用于访问上下文,避免 props drilling。
tsx
import React, { useContext, createContext } from 'react';
// 创建上下文
const ThemeContext = createContext<'light' | 'dark'>('light');
function ThemedButton() {
  // 访问上下文
  const theme = useContext(ThemeContext);
  return (
    <button style={{ background: theme === 'dark' ? '#333' : '#fff', color: theme === 'dark' ? '#fff' : '#333' }}>
      {theme === 'dark' ? 'Dark Mode' : 'Light Mode'}
    </button>
  );
}

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <ThemedButton />
    </ThemeContext.Provider>
  );
}
  1. useReducer
    用于更复杂的状态管理,类似于Redux。
tsx
import React, { useReducer } from 'react';

// 定义状态类型
interface CounterState {
  count: number;
}

// 定义动作类型
type CounterAction = 
  | { type: 'increment' }
  | { type: 'decrement' };

// 定义reducer函数
function counterReducer(state: CounterState, action: CounterAction): CounterState {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error('Unexpected action');
  }
}

function Counter() {
  // 使用useReducer
  const [state, dispatch] = useReducer(counterReducer, { count: 0 });

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </div>
  );
}
  1. useRef
    用于访问DOM元素或保存可变值。
tsx
import React, { useRef, useEffect } from 'react';

function TextInputWithFocusButton() {
  // 创建ref
  const inputEl = useRef<HTMLInputElement>(null);

  const focusInput = () => {
    // 访问DOM元素
    inputEl.current?.focus();
  };

  return (
    <div>
      <input ref={inputEl} type="text" />
      <button onClick={focusInput}>Focus the input</button>
    </div>
  );
}
  1. useCallback
    用于缓存函数引用,避免不必要的重新渲染。
tsx
import React, { useState, useCallback } from 'react';

interface ButtonProps {
  onClick: () => void;
  children: React.ReactNode;
}

function Button({ onClick, children }: ButtonProps) {
  console.log('Button re-rendered');
  return <button onClick={onClick}>{children}</button>;
}

function App() {
  const [count, setCount] = useState<number>(0);

  // 缓存函数
  const increment = useCallback(() => {
    setCount(count + 1);
  }, [count]);

  return (
    <div>
      <p>Count: {count}</p>
      <Button onClick={increment}>Increment</Button>
    </div>
  );
}
  1. useMemo
    用于缓存计算结果,避免不必要的重复计算。
tsx
import React, { useState, useMemo } from 'react';

interface ExpensiveCalculationProps {
  num: number;
}

function ExpensiveCalculation({ num }: ExpensiveCalculationProps) {
  // 缓存计算结果
  const result = useMemo(() => {
    console.log('Calculating...');
    return num * 2;
  }, [num]);

  return <div>Result: {result}</div>;
}

function App() {
  const [count, setCount] = useState<number>(0);
  const [num, setNum] = useState<number>(10);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment Count</button>
      <p>Num: {num}</p>
      <button onClick={() => setNum(num + 1)}>Increment Num</button>
      <ExpensiveCalculation num={num} />
    </div>
  );
}
  1. useLayoutEffect
    用于在DOM更新后同步执行副作用,类似于useEffect但执行时机不同。
tsx
import React, { useRef, useLayoutEffect } from 'react';

function LayoutEffectExample() {
  const ref = useRef<HTMLDivElement>(null);

  useLayoutEffect(() => {
    if (ref.current) {
      const rect = ref.current.getBoundingClientRect();
      console.log('Element position:', rect);
    }
  }, []);

  return <div ref={ref}>Hello World</div>;
}
  1. useDebugValue
    用于在React开发者工具中显示自定义hook的标签。
tsx
import { useDebugValue, useState } from 'react';

function useCounter(initialValue: number) {
  const [count, setCount] = useState<number>(initialValue);

  // 在开发者工具中显示标签
  useDebugValue(`Count: ${count}`);

  return [count, setCount] as const;
}

function Counter() {
  const [count, setCount] = useCounter(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

react hooks原理

  1. hooks的数据结构
    每个组件都有一个hooks链表,每个hook对应一个Hook对象。
    Hooks以链表形式存储在Fiber节点的memoizedState上。
    源码位置

    • memoizedState:
      • useState:保存当前状态值
      • useEffect:保存 { create, destroy, deps, tag } 对象
      • useRef:保存 { current } 对象
    ts
    type Hook = {
      memoizedState: any,      // 存储当前状态(state/effect/deps等)
      baseState: any,          // 更新计算的基础状态
      baseQueue: Update<any, any> | null, // 未处理的更新队列
      queue: UpdateQueue<any, any> | null, // 更新队列(存储 setState 的 action)
      next: Hook | null,       // 指向下一个 Hook(形成链表)
    };

    1.1 useState 实现
    通过Fiber.memoizedState判断是否是mount阶段,如果是mount阶段,就调用mountState,否则调用updateState。
    以下是mount阶段伪代码,mountState源码

    js
    function mountState(initialState) {
      // 1. 创建新的Hook对象
      const hook = mountWorkInProgressHook();
      // 2. 初始化状态
      hook.memoizedState = initialState;
      // 3. 创建更新队列
      const queue = {
        pending: null,
        dispatch: null,
        lastRenderedState: initialState
      };
      hook.queue = queue;
      // 4. 创建dispatch函数(绑定Fiber和队列)
      const dispatch = (queue.dispatch = dispatchSetState.bind(
        null,
        currentlyRenderingFiber,
        queue
      ));
      return [hook.memoizedState, dispatch];
    }

    以下是update阶段伪代码,updateState源码

    js
    function updateState() {
      return updateReducer(basicStateReducer);
    }
    function updateReducer(reducer) {
      // 1. 获取现有Hook对象
      const hook = updateWorkInProgressHook();
      // 2. 处理更新队列
      if (hook.queue.pending) {
        // ...计算新状态
      }
      return [hook.memoizedState, hook.queue.dispatch];
    }

    1.2 useState 实现整体过程useState 实现整体过程

react 渲染过程

  1. 初始化阶段react版本为19.1.0

    js
     export function createRoot(container, options) {
       // 验证容器有效性
       if (!isValidContainer(container)) {
         throw new Error('Target container is not a DOM element.');
       }
       // 创建FiberRoot和root
       const root = createContainer(
         container,
         ConcurrentRoot, // React 18默认并发模式
         null,
         isStrictMode,
         concurrentUpdatesByDefaultOverride,
         identifierPrefix,
         onRecoverableError,
         transitionCallbacks,
       );
       // 标记容器已被使用
       container._reactRootContainer = root;
       // 绑定所有支持的事件
       const rootContainerElement = container.nodeType === COMMENT_NODE
         ? container.parentNode
         : container;
       // React事件委托机制的基础,通过在根容器集中注册事件监听器,\n React能够实现高效的事件处理和合成事件系统,统一管理不同浏览器的事件兼容性问题
       listenToAllSupportedEvents(rootContainerElement);
       return new ReactDOMRoot(root);
     }
    • createContainer入口
      创建 Fiber 根节点,初始化更新队列initializeUpdateQueue(HostRootFiber),FiberRoot.current = HostRootFiber
      fiberRoot: 整个 React 应用的「根容器」,唯一一个。
      rootFiber: 组件树的根节点对应的 Fiber 对象(HostRootFiber),可能存在多个。
      以下是伪代码,createContainer源码
    js
     function createContainer(
       containerInfo: Container,
       tag: RootTag,
       hydrate: boolean,
       hydrationCallbacks: null | SuspenseHydrationCallbacks,
     ): FiberRoot {
       // 1. 创建 FiberRoot 实例
       const root: FiberRoot = {
         containerInfo, // 真实 DOM 容器(如 div#root)
         current: null, // 当前激活的 Fiber 树(current 树)
         pendingLanes: NoLanes, // 待处理的更新优先级队列
         // 其他全局状态...
       };
       // 2. 创建 HostRootFiber(根 Fiber 节点)
       const uninitializedFiber = createHostRootFiber(tag);
       root.current = uninitializedFiber; // FiberRoot 关联 current 树
       uninitializedFiber.stateNode = root; // Fiber 节点反向关联 FiberRoot
       return root;
     }
    • 双缓冲机制
      双缓冲机制是指在渲染过程中,使用两个缓冲区来存储中间结果,避免直接渲染到屏幕上导致的闪烁问题。
      在更新过程中的commit阶段,最后root.current = workInProgressRootFiber,从而实现平滑的渲染效果。
      Fiber数据结构
      双缓冲机制
  2. 触发渲染

    js
      ReactDOMRoot.prototype.render = function(children) {
        updateContainer(children, root, null, null);
      };
    js
    function updateContainer(
      element,
      container,
      parentComponent,
      callback,
    ){
      // 1. 获取根 Fiber 节点(HostRootFiber)
      const current = container.current;
      // 2. 为初始更新分配优先级(同步优先级,确保立即执行)
      const lane = requestUpdateLane(current);
      // 3. 创建更新对象(内容为 <App /> 组件树)
      const update = createUpdate(lane, null, callback);
      update.payload = { element }; // 更新内容:待渲染的组件树
      // 4. 加入更新队列
      enqueueUpdate(current, update);
      // 5. 调度更新执行
      scheduleUpdateOnFiber(current, lane, node);
    }
  3. 调度协调阶段

    js
    function scheduleUpdateOnFiber(fiber, lane, eventTime) {
      //标记root 为更新状态
     markRootUpdated(root, lane);
     // 确保根节点被调度(调度入口)
     ensureRootIsScheduled(root);
    }
    js
    function ensureRootIsScheduled(root) {
     // 触发微任务调度
     ensureScheduleIsScheduled()
    }

    以下是伪代码,ensureScheduleIsScheduled源码

    js
    function ensureScheduleIsScheduled() {
     // 执行微任务
     if (!didScheduleMicrotask) {
       didScheduleMicrotask = true;
       scheduleImmediateRootScheduleTask();
     }
    }

    scheduleImmediateRootScheduleTask函数
    环境不同,它可能会使用微任务(microtask)或者调度器(Scheduler)来处理根节点的调度
    以下是伪代码,scheduleImmediateRootScheduleTask源码

    js
    function scheduleImmediateRootScheduleTask() {
     // 1. 检查是否支持微任务
     if (supportsMicrotasks) {
       // 2. 使用微任务调度
       processRootScheduleInMicrotask(performRootSchedule);
     } else {
       // 3. 使用调度器调度
       scheduleCallback(ImmediateSchedulerPriority, performRootSchedule);
     }
    }

    本次环境默认进入微任务调度,不使用Schedule调度
    调用processRootScheduleInMicrotask后,进入flushSyncWorkAcrossRoots_impl函数,处理同步工作
    以下是伪代码,flushSyncWorkAcrossRoots_impl源码

    js
    function flushSyncWorkAcrossRoots_impl(syncTransitionLanes, isDuringMicrotask) {
      // 1. 检查是否有同步工作
      if (!mightHavePendingSyncWork) return;
      // 2. 重置标志
      mightHavePendingSyncWork = false;
      // 3. 遍历所有根节点
      let root = firstScheduledRoot;
      while (root !== null) {
        // 4. 检查是否需要同步处理
        if (
          syncTransitionLanes !== NoLanes ||
          includesSyncLane(root.pendingLanes) ||
          (enableGestureTransition && isGestureRender(root.pendingLanes))
        ) {
          // 5. 执行同步工作
          performSyncWorkOnRoot(root);
        }
        root = root.next;
      }
    }
  • 以下是performSyncWorkOnRoot到completeUnitOfWork的流程
performSyncWorkOnRoot到completeUnitOfWork流程
  1. commit 阶段 completeUnitOfWork函数
    完成工作单元的处理并向上回溯 Fiber 树
    以下是伪代码,completeUnitOfWork源码
    js
    function completeUnitOfWork(unitOfWork: Fiber): void {
      let completedWork: Fiber = unitOfWork;
        do {
          // 1. 检查未完成标志
          if ((completedWork.flags & Incomplete) !== NoFlags) {
            unwindUnitOfWork(completedWork, ...);
            return;
          }
          // 2. 获取当前节点和父节点
          const current = completedWork.alternate;
          const returnFiber = completedWork.return;
          // 3. 执行完成工作
          const next = completeWork(current, completedWork, entangledRenderLanes);
          // 4. 处理新产生的工作
          if (next !== null) {
            workInProgress = next;
            return;
          }
          // 5. 处理兄弟节点
          const siblingFiber = completedWork.sibling;
          if (siblingFiber !== null) {
            workInProgress = siblingFiber;
            return;
          }
          // 6. 回溯到父节点
          completedWork = returnFiber;
          workInProgress = completedWork;
        } while (completedWork !== null);
        // 7. 标记根节点完成
        if (workInProgressRootExitStatus === RootInProgress) {
          workInProgressRootExitStatus = RootCompleted;
        }
    }

completeWork函数
创建/更新 DOM 节点并设置属性、构建 effect 链表供提交阶段使用
completeWork源码

commitRootWhenReady、commitRoot函数
在finishConcurrentRender函数中触发
提交根节点的 effect 链表,将 DOM 变更应用到真实 DOM 上
finishConcurrentRender源码commitRoot源码 以下是commitRoot函数的伪代码

js
function commitRoot(root) {
  const finishedWork = root.finishedWork;
  // 执行 BeforeMutation 阶段生命周期
  // 读取当前 DOM 状态
  commitBeforeMutationEffects(finishedWork);
  // 执行 DOM 操作
  // 插入/更新/删除节点
  // 更新 DOM 属性
  // 文本节点内容变更
  commitMutationEffects(finishedWork, root);
  // 切换当前 Fiber 树
  root.current = finishedWork;
  // 执行 Layout 阶段生命周期
  // 生命周期:componentDidMount/componentDidUpdate
  // Hook useLayoutEffect 回调
  commitLayoutEffects(finishedWork, root);
  // 清理工作
  root.finishedWork = null;
}