本文主要是介绍react源码解析17.context,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
## react源码解析17.context
#### 视频课程(高效学习):[进入课程](https://xiaochen1024.com/series/60b1b600712e370039088e24/60b1b636712e370039088e25)
#### 课程目录:
[1.开篇介绍和面试题](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b311cf10a4003b634719)
[2.react的设计理念](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b31ccf10a4003b63471a)
[3.react源码架构](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b328cf10a4003b63471b)
[4.源码目录结构和调试](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b32ecf10a4003b63471c)
[5.jsx&核心api](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b334cf10a4003b63471d)
[6.legacy和concurrent模式入口函数](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b33acf10a4003b63471e)
[7.Fiber架构](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b340cf10a4003b63471f)
[8.render阶段](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b348cf10a4003b634720)
[9.diff算法](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b354cf10a4003b634721)
[10.commit阶段](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b360cf10a4003b634722)
[11.生命周期](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b367cf10a4003b634723)
[12.状态更新流程](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b36ecf10a4003b634724)
[13.hooks源码](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b374cf10a4003b634725)
[14.手写hooks](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b37acf10a4003b634726)
[15.scheduler&Lane](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b556cf10a4003b634727)
[16.concurrent模式](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b55ccf10a4003b634728)
[17.context](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b564cf10a4003b634729)
[18事件系统](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b56ccf10a4003b63472a)
[19.手写迷你版react](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b57bcf10a4003b63472b)
[20.总结&第一章的面试题解答](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b581cf10a4003b63472c)
[21.demo](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b587cf10a4003b63472d)
#### 查看视频调试demo_7
#### context流程图
![react源码17.1](https://gitee.com/xiaochen1024/assets/raw/master/assets/20210529105951.png)
![react源码17.2](https://gitee.com/xiaochen1024/assets/raw/master/assets/20210529105954.png)
#### cursor/valueStack
react源码中存在一个valueStack和valueCursor用来记录context的历史信息和当前context,另外还有一个didPerformWorkStackCursor用来表示当前的context有没有变化
```js
//ReactFiberNewContext.new.js
const valueCursor: StackCursor
= createCursor(null);
```
```js
const didPerformWorkStackCursor: StackCursor = createCursor(false);
```
```js
//ReactFiberStack.new.js
const valueStack: Array = [];
```
```js
function pushProvider(providerFiber, nextValue) {
var context = providerFiber.type._context;
{
push(valueCursor, context._currentValue, providerFiber);
context._currentValue = nextValue;
}
}
```
```js
function popProvider(providerFiber) {
var currentValue = valueCursor.current;
pop(valueCursor, providerFiber);
var context = providerFiber.type._context;
{
context._currentValue = currentValue;
}
}
```
- 在render阶段调用updateContextProvider的时候会执行pushProvider,将新的值push进valueStack中
- 在commit阶段调用completeWork的时候会执行popProvider,将栈顶context pop出来,
为什么会有这样一个机制呢,因为我们的context是跨层级的,在之前讲到render阶段和commit阶段的时候,我们会以深度优先遍历的方式遍历节点,如果涉及跨层级读取状态就有点力不从心了,就需要一层一层往下传递我们的props,所以我们可以用一个stack记录我们的context,在render阶段pushProvider,在commit阶段popProvider,在每个具体的层级能根据valueCursor取当前value
#### createContext
```js
export function createContext(
defaultValue: T,
calculateChangedBits: ?(a: T, b: T) => number,
): ReactContext {
if (calculateChangedBits === undefined) {//可以传入计算bit的函数
calculateChangedBits = null;
} else {
//...
}
const context: ReactContext = {
$$typeof: REACT_CONTEXT_TYPE,
_calculateChangedBits: calculateChangedBits,//计算value变化的函数
_currentValue: defaultValue,//dom环境的value
_currentValue2: defaultValue,//art环境的value
_threadCount: 0,
Provider: (null: any),
Consumer: (null: any),
};
context.Provider = {
$$typeof: REACT_PROVIDER_TYPE,
_context: context,
};
if (__DEV__) {
} else {
context.Consumer = context;
}
return context;
}
//示例
const NameChangedBits = 0b01;
const AgeChangedBits = 0b10;
const AppContext = createContext({}, (prevValue, nextValue) => {
let result = 0;
if (prevValue.name !== nextValue.name) {
result |= NameChangedBits;
};
if (prevValue.age !== nextValue.age) {
result |= AgeChangedBits;
};
return result;
});
```
在简化之后的createContext中可以看到,context和Provider、Consumer的关系是这样的:
```js
context.Provider = {
$$typeof: REACT_PROVIDER_TYPE,
_context: context,
};
context.Consumer = context;
```
#### useContext
useContext会调用readContext,readContext会创建dependce,加入当前fiber的dependencies链表中
```js
function readContext(context, observedBits) {
{
if (lastContextWithAllBitsObserved === context) ; else if (observedBits === false || observedBits === 0) ; else {
var resolvedObservedBits;
//生成resolvedObservedBits
if (typeof observedBits !== 'number' || observedBits === MAX_SIGNED_31_BIT_INT) {
lastContextWithAllBitsObserved = context;
resolvedObservedBits = MAX_SIGNED_31_BIT_INT;
} else {
resolvedObservedBits = observedBits;
}
var contextItem = {//生成dependce
context: context,
observedBits: resolvedObservedBits,
next: null
};
if (lastContextDependency === null) {
//...
lastContextDependency = contextItem;
currentlyRenderingFiber.dependencies = {//dependencies链表的第一个
lanes: NoLanes,
firstContext: contextItem,
responders: null
};
} else {
lastContextDependency = lastContextDependency.next = contextItem;//加入dependencies链表
}
}
return context._currentValue ;
}
```
#### provider/customer
在render阶段会调用updateContextProvider,注意几个关键的步骤
- pushProvider:将当前context加入valueStack
- calculateChangedBits:useContext可以设置observedBits,没有设置的话就是MAX_SIGNED_31_BIT_INT,也就是31位1,用于计算changedBits,这个计算context是否变化的过程就发生在calculateChangedBits函数中,用这样的方式可以提高context变化之后的性能
- bailoutOnAlreadyFinishedWork/propagateContextChange:如果changedBits没有改变则走bailoutOnAlreadyFinishedWork的逻辑,跳过当前节点的更新,如果改变则执行propagateContextChange
```js
function updateContextProvider(current, workInProgress, renderLanes) {
var providerType = workInProgress.type;
var context = providerType._context;
var newProps = workInProgress.pendingProps;
var oldProps = workInProgress.memoizedProps;
var newValue = newProps.value;
//...
pushProvider(workInProgress, newValue);
if (oldProps !== null) {
var oldValue = oldProps.value;
var changedBits = calculateChangedBits(context, newValue, oldValue);
if (changedBits === 0) {//context没有改变
if (oldProps.children === newProps.children && !hasContextChanged()) {
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
}
} else {//context改变了
propagateContextChange(workInProgress, context, changedBits, renderLanes);
}
}
var newChildren = newProps.children;
reconcileChildren(current, workInProgress, newChildren, renderLanes);
return workInProgress.child;
}
```
```js
function calculateChangedBits(context, newValue, oldValue) {
if (objectIs(oldValue, newValue)) {
//没有改变
return 0;
} else {
var changedBits = typeof context._calculateChangedBits === 'function' ? context._calculateChangedBits(oldValue, newValue) : MAX_SIGNED_31_BIT_INT;
{
if ((changedBits & MAX_SIGNED_31_BIT_INT) !== changedBits) {
error('calculateChangedBits: Expected the return value to be a ' + '31-bit integer. Instead received: %s', changedBits);
}
}
return changedBits | 0;
}
}
//示例
const NameChangedBits = 0b01;
const AgeChangedBits = 0b10;
const AppContext = createContext({}, (prevValue, nextValue) => {
let result = 0;
if (prevValue.name !== nextValue.name) {
result |= NameChangedBits;
};
if (prevValue.age !== nextValue.age) {
result |= AgeChangedBits;
};
return result;
});
```
```js
function propagateContextChange(workInProgress, context, changedBits, renderLanes) {
var fiber = workInProgress.child;
if (fiber !== null) {
fiber.return = workInProgress;//fiber不存在 就找父节点
}
while (fiber !== null) {
var nextFiber = void 0;//遍历fiber
var list = fiber.dependencies;
if (list !== null) {
nextFiber = fiber.child;
var dependency = list.firstContext;
while (dependency !== null) {//遍历dependencies链表
if (dependency.context === context && (dependency.observedBits & changedBits) !== 0) {
//有变化
if (fiber.tag === ClassComponent) {
//创建新的update
var update = createUpdate(NoTimestamp, pickArbitraryLane(renderLanes));
update.tag = ForceUpdate;
enqueueUpdate(fiber, update);
}
fiber.lanes = mergeLanes(fiber.lanes, renderLanes);//合并优先级
var 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;
}
}
//...
nextFiber = fiber.sibling;
} else {
nextFiber = fiber.child;
}
//...
fiber = nextFiber;
}
}
```
updateContextConsumer关键的代码如下,执行prepareToReadContext判断优先级是否足够加入当前这次render,readContext取到当前context的value
```js
function updateContextConsumer(current, workInProgress, renderLanes) {
var context = workInProgress.type;
//...
prepareToReadContext(workInProgress, renderLanes);
var newValue = readContext(context, newProps.unstable_observedBits);
var newChildren;
{
ReactCurrentOwner$1.current = workInProgress;
setIsRendering(true);
newChildren = render(newValue);
setIsRendering(false);
}
//...
workInProgress.flags |= PerformedWork;
reconcileChildren(current, workInProgress, newChildren, renderLanes);
return workInProgress.child;
}
```
这篇关于react源码解析17.context的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!