react Provider、Consumer原理解析

Context 状态管理

  • 在fiber树创建过程中, 如果使用了Context api(具体来说是使用Context.Provider,Context.Consumer,Class.contextType等api), react内部会维护一个栈来保存提供者(Context.Provider)的状态, 供给消费者(Context.Consumer)使用
export type StackCursor<T> = {| current: T |};

// 维护一个全局stack
const valueStack: Array<any> = [];
let index = -1;

// 一个工厂函数, 创建StackCursor对象
function createCursor<T>(defaultValue: T): StackCursor<T> {
  return {
    current: defaultValue,
  };
}
function isEmpty(): boolean {
  return index === -1;
}
// 出栈
function pop<T>(cursor: StackCursor<T>, fiber: Fiber): void {
  if (index < 0) {
    return;
  }
  cursor.current = valueStack[index];
  valueStack[index] = null;
  index--;
}
// 入栈
function push<T>(cursor: StackCursor<T>, value: T, fiber: Fiber): void {
  index++;
  valueStack[index] = cursor.current;
  cursor.current = value;
}

ReactFiberNewContext.js:

// 定义全局 valueCursor, 用于管理<Context.Provider/>组件的value
const valueCursor: StackCursor<mixed> = createCursor(null);


// 将context当前的值保存到valueCursor中, 并设置context._currentValue为最新值
// 运行完成之后context为最新状态
export function pushProvider<T>(providerFiber: Fiber, nextValue: T): void {
  //获取fiber上的context
  const context: ReactContext<T> = providerFiber.type._context;
  //入栈操作,将context上一次的值入栈(首次valueCursor.current为null,context._currentValue为创建context时的默认值)
  push(valueCursor, context._currentValue, providerFiber);
  //更新context值为最新值
  context._currentValue = nextValue;
}

// 取出valueCursor中保存的旧值, 设置到context._currentValue上.
// 运行完成之后context恢复到上一个状态
export function popProvider(providerFiber: Fiber): void {
  const currentValue = valueCursor.current;
  //出栈操作
  pop(valueCursor, providerFiber);
  const context: ReactContext<any> = providerFiber.type._context;
  context._currentValue = currentValue;
}

示例

const MyContext = React.createContext(0);

export default function App() {
  return (
    // 第一级
    <MyContext.Provider value={1}>
      <MyContext.Consumer>
        {value1 => (
          //第二级嵌套
          <MyContext.Provider value={2}>
            <MyContext.Consumer>
              {value2 => (
                // 第三级嵌套
                <MyContext.Provider value={3}>
                  <MyContext.Consumer>
                    {value3 => (
                      <span>
                        {value1}-{value2}-{value3}
                      </span>
                    )}
                  </MyContext.Consumer>
                </MyContext.Provider>
              )}
            </MyContext.Consumer>
          </MyContext.Provider>
        )}
      </MyContext.Consumer>
    </MyContext.Provider>
  );
}

在beginWork阶段进行入栈

  • reconciler过程中, 每当遇到Context.Provider类型的节点, 则会执行pushProvider
    在这里插入图片描述

completeWork进行出栈

  • beiginWork进行节点遍历,completeWork进行遍历节点的回朔,恰好和栈的出入逻辑上相符
  • 每当遇到Context.Provider类型的节点, 则会执行popProvider
    在这里插入图片描述

创建 Context

  • 其初始值保存在context._currentValue
  • 创建了context.Provider, context.Consumer2 个ReactElement对象,使得可以通过<MyContext.Provider value={/* 某个值 */}>方式使用
export function createContext<T>(
  defaultValue: T,
  calculateChangedBits: ?(a: T, b: T) => number,
): ReactContext<T> {
  if (calculateChangedBits === undefined) {
    calculateChangedBits = null;
  }
  const context: ReactContext<T> = {
    $$typeof: REACT_CONTEXT_TYPE,
    _calculateChangedBits: calculateChangedBits,
    // As a workaround to support multiple concurrent renderers, we categorize
    // some renderers as primary and others as secondary. We only expect
    // there to be two concurrent renderers at most: React Native (primary) and
    // Fabric (secondary); React DOM (primary) and React ART (secondary).
    // Secondary renderers store their context values on separate fields.
    _currentValue: defaultValue,
    //_currentValue2为concurrent模式准备的
    _currentValue2: defaultValue,
    _threadCount: 0,
    Provider: (null: any),
    Consumer: (null: any),
  };

  context.Provider = {
    $$typeof: REACT_PROVIDER_TYPE,
    _context: context,
  };
  context.Consumer = context;
  return context;
}

初次构造

  • 在fiber树渲染时, 在beginWork中对ContextProvider类型的节点处理
function beginWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
): Fiber | null {
  const updateLanes = workInProgress.lanes;
  workInProgress.lanes = NoLanes;
  // ...省略无关代码
  switch (workInProgress.tag) {
    //针对对应的fiber节点进行处理
    case ContextProvider:
      return updateContextProvider(current, workInProgress, renderLanes);
    case ContextConsumer:
      return updateContextConsumer(current, workInProgress, renderLanes);
  }
}

function updateContextProvider(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
) {
  // ...
  const providerType: ReactProviderType<any> = workInProgress.type;
  const context: ReactContext<any> = providerType._context;

  const newProps = workInProgress.pendingProps;
  const oldProps = workInProgress.memoizedProps;
  // 接收新value
  const newValue = newProps.value;

  // 更新 ContextProvider._currentValue
  pushProvider(workInProgress, newValue);
  
  //说明是对比更新阶段
  if (oldProps !== null) {
    // ... 更新context的逻辑
  }
  
  //进行子节点的比较
  const newChildren = newProps.children;
  reconcileChildren(current, workInProgress, newChildren, renderLanes);
  return workInProgress.child;
}

消费 Context
updateContextConsumer

function updateContextConsumer(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
) {
  //context作为fiber的属性存在
  let context: ReactContext<any> = workInProgress.type;
  const newProps = workInProgress.pendingProps;
  const render = newProps.children;

  // 读取context
  prepareToReadContext(workInProgress, renderLanes);
  const newValue = readContext(context, newProps.unstable_observedBits);
  let newChildren;

  // ...
}

prepareToReadContext、readContext

  • Consumer时每遇到一个相同类型的context,构造成链表将当前context挂载到链表上,并返回当前context存储的value
  • 因为context的值是通过栈的结构来保存的,consumer拿到的value都是provider预先存入的,所以有多个provider,consumer拿到的是离自己最近的provider
  • 因为相同context创造的consumer对应的都是一个fiber节点,所以多个consumer的context都会挂载到同一个fiber的依赖上
// ... 省略无关代码
export function prepareToReadContext(
  workInProgress: Fiber,
  renderLanes: Lanes,
): void {
  // 1. 设置全局变量, 为readContext做准备
  currentlyRenderingFiber = workInProgress;
  lastContextDependency = null;
  lastContextWithAllBitsObserved = null;
  
  //首次消费不进入,在对比更新时才进入
  const dependencies = workInProgress.dependencies;
  if (dependencies !== null) {
    const firstContext = dependencies.firstContext;
    if (firstContext !== null) {
      //进行更新标识
      if (includesSomeLane(dependencies.lanes, renderLanes)) {
        // Context list has a pending update. Mark that this fiber performed work.
        markWorkInProgressReceivedUpdate();
      }
      // Reset the work-in-progress list
      dependencies.firstContext = null;
    }
  }
}
// ... 
export function readContext<T>(
  context: ReactContext<T>,
  observedBits: void | number | boolean,
): T {
  const contextItem = {
    context: ((context: any): ReactContext<mixed>),
    observedBits: resolvedObservedBits,
    next: null,
  };
  // 1. 构造一个contextItem, 加入到 workInProgress.dependencies链表之后
  if (lastContextDependency === null) {
    lastContextDependency = contextItem;
    //currentlyRenderingFiber即workInProgress
    currentlyRenderingFiber.dependencies = {
      lanes: NoLanes,
      firstContext: contextItem,
      responders: null,
    };
  } else {
    //构造链表
    lastContextDependency = lastContextDependency.next = contextItem;
  }
  // 2. 返回 currentValue
  return isPrimaryRenderer ? context._currentValue : context._currentValue2;
}

对比更新阶段

进入updateContextProvider

function updateContextProvider(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
) {
  const providerType: ReactProviderType<any> = workInProgress.type;
  const context: ReactContext<any> = providerType._context;

  const newProps = workInProgress.pendingProps;
  const oldProps = workInProgress.memoizedProps;

  const newValue = newProps.value;
  
  //因为completeWork中会出栈,所以再次遇到Provider能够和初次入栈逻辑相同
  pushProvider(workInProgress, newValue);

  if (oldProps !== null) {
    // 更新阶段进入
    const oldValue = oldProps.value;
    // 对比 newValue 和 oldValue
    // 和Object.is比较效果相同
    const changedBits = calculateChangedBits(context, newValue, oldValue);
    if (changedBits === 0) {
      // value没有变动, 进入 Bailout 逻辑
      if (
        oldProps.children === newProps.children &&
        !hasLegacyContextChanged()
      ) {
        //当前fiber节点无需更新, 循环检测子节点是否需要更新
        return bailoutOnAlreadyFinishedWork(
          current,
          workInProgress,
          renderLanes,
        );
      }
    } else {
      // value变动, 查找对应的consumers, 并使其能够被更新
      propagateContextChange(workInProgress, context, changedBits, renderLanes);
    }
  }
  // ... 
}

propagateContextChange

  • 当Provider的value变动后,从该fiber开始向下查找依赖的consumer
  • 比较依赖的consumer挂载的context链表,如果需要更新,从该fiber开始,往上一直查找父节点并打上更新的标识
export function propagateContextChange(
  workInProgress: Fiber,
  context: ReactContext<mixed>,
  changedBits: number,
  renderLanes: Lanes,
): void {
  //从当前Provider的第一个子节点开始查找
  let fiber = workInProgress.child;
  if (fiber !== null) {
    // Set the return pointer of the child to the work-in-progress fiber.
    fiber.return = workInProgress;
  }
  
  //往下遍历fiber,找到所有有依赖Provider的context
  while (fiber !== null) {
    let nextFiber;
    const list = fiber.dependencies;
    if (list !== null) {
      nextFiber = fiber.child;
      let dependency = list.firstContext;
      //查找comuser挂载的所有context
      while (dependency !== null) {
        // 检查dependency中依赖的context是否需要更新
        if (
          dependency.context === context &&
          (dependency.observedBits & changedBits) !== 0
        ) {
          // 符合条件, 安排调度
          if (fiber.tag === ClassComponent) {
            // class 组件需要创建一个update对象, 添加到updateQueue队列
            const update = createUpdate(
              NoTimestamp,
              pickArbitraryLane(renderLanes),
            );
            update.tag = ForceUpdate; // 注意ForceUpdate, 保证class组件一定执行render
            enqueueUpdate(fiber, update);
          }
          fiber.lanes = mergeLanes(fiber.lanes, renderLanes);
          const alternate = fiber.alternate;
          if (alternate !== null) {
            alternate.lanes = mergeLanes(alternate.lanes, renderLanes);
          }
          // 往上遍历,标记更新
          scheduleWorkOnParentPath(fiber.return, renderLanes);

          // 标记优先级
          list.lanes = mergeLanes(list.lanes, renderLanes);

          // 退出查找
          break;
        }
        dependency = dependency.next;
      }
    }

    // ...

    fiber = nextFiber;
  }
}

scheduleWorkOnParentPath

  • 从consumer节点开始, 向上遍历, 修改父路径上所有节点的fiber.childLanes属性, 表明其子节点有改动, 子节点会进入更新逻辑
export function scheduleWorkOnParentPath(
  parent: Fiber | null,
  renderLanes: Lanes,
) {
  // Update the child lanes of all the ancestors, including the alternates.
  let node = parent;
  while (node !== null) {
    const alternate = node.alternate;
    if (!isSubsetOfLanes(node.childLanes, renderLanes)) {
      node.childLanes = mergeLanes(node.childLanes, renderLanes);
      if (alternate !== null) {
        alternate.childLanes = mergeLanes(
          alternate.childLanes,
          renderLanes,
        );
      }
    } else if (
      alternate !== null &&
      !isSubsetOfLanes(alternate.childLanes, renderLanes)
    ) {
      alternate.childLanes = mergeLanes(alternate.childLanes, renderLanes);
    } else {
      // Neither alternate was updated, which means the rest of the
      // ancestor path already has sufficient priority.
      break;
    }
    node = node.return;
  }
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值