Skip to content

Instantly share code, notes, and snippets.

@Jokcy
Created December 12, 2018 07:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Jokcy/c6b0e36383dbfc0e0e254f2ffe416a6b to your computer and use it in GitHub Desktop.
Save Jokcy/c6b0e36383dbfc0e0e254f2ffe416a6b to your computer and use it in GitHub Desktop.
react next context readContext and ...
export function propagateContextChange(
workInProgress: Fiber,
context: ReactContext<mixed>,
changedBits: number,
renderExpirationTime: ExpirationTime,
): void {
let fiber = workInProgress.child;
if (fiber !== null) {
// Set the return pointer of the child to the work-in-progress fiber.
fiber.return = workInProgress;
}
while (fiber !== null) {
let nextFiber;
// Visit this fiber.
let dependency = fiber.firstContextDependency;
if (dependency !== null) {
do {
// Check if the context matches.
if (
dependency.context === context &&
(dependency.observedBits & changedBits) !== 0
) {
// Match! Schedule an update on this fiber.
if (fiber.tag === ClassComponent) {
// Schedule a force update on the work-in-progress.
const update = createUpdate(renderExpirationTime);
update.tag = ForceUpdate;
// TODO: Because we don't have a work-in-progress, this will add the
// update to the current fiber, too, which means it will persist even if
// this render is thrown away. Since it's a race condition, not sure it's
// worth fixing.
enqueueUpdate(fiber, update);
}
if (
fiber.expirationTime === NoWork ||
fiber.expirationTime > renderExpirationTime
) {
fiber.expirationTime = renderExpirationTime;
}
let alternate = fiber.alternate;
if (
alternate !== null &&
(alternate.expirationTime === NoWork ||
alternate.expirationTime > renderExpirationTime)
) {
alternate.expirationTime = renderExpirationTime;
}
// Update the child expiration time of all the ancestors, including
// the alternates.
let node = fiber.return;
while (node !== null) {
alternate = node.alternate;
if (
node.childExpirationTime === NoWork ||
node.childExpirationTime > renderExpirationTime
) {
node.childExpirationTime = renderExpirationTime;
if (
alternate !== null &&
(alternate.childExpirationTime === NoWork ||
alternate.childExpirationTime > renderExpirationTime)
) {
alternate.childExpirationTime = renderExpirationTime;
}
} else if (
alternate !== null &&
(alternate.childExpirationTime === NoWork ||
alternate.childExpirationTime > renderExpirationTime)
) {
alternate.childExpirationTime = renderExpirationTime;
} else {
// Neither alternate was updated, which means the rest of the
// ancestor path already has sufficient priority.
break;
}
node = node.return;
}
}
nextFiber = fiber.child;
dependency = dependency.next;
} while (dependency !== null);
} else if (fiber.tag === ContextProvider) {
// Don't scan deeper if this is a matching provider
nextFiber = fiber.type === workInProgress.type ? null : fiber.child;
} else {
// Traverse down.
nextFiber = fiber.child;
}
if (nextFiber !== null) {
// Set the return pointer of the child to the work-in-progress fiber.
nextFiber.return = fiber;
} else {
// No child. Traverse to next sibling.
nextFiber = fiber;
while (nextFiber !== null) {
if (nextFiber === workInProgress) {
// We're back to the root of this subtree. Exit.
nextFiber = null;
break;
}
let sibling = nextFiber.sibling;
if (sibling !== null) {
// Set the return pointer of the sibling to the work-in-progress fiber.
sibling.return = nextFiber.return;
nextFiber = sibling;
break;
}
// No more siblings. Traverse up.
nextFiber = nextFiber.return;
}
}
fiber = nextFiber;
}
}
export function prepareToReadContext(
workInProgress: Fiber,
renderExpirationTime: ExpirationTime,
): void {
currentlyRenderingFiber = workInProgress;
lastContextDependency = null;
lastContextWithAllBitsObserved = null;
// Reset the work-in-progress list
workInProgress.firstContextDependency = null;
}
export function readContext<T>(
context: ReactContext<T>,
observedBits: void | number | boolean,
): T {
if (lastContextWithAllBitsObserved === context) {
// Nothing to do. We already observe everything in this context.
} else if (observedBits === false || observedBits === 0) {
// Do not observe any updates.
} else {
let resolvedObservedBits; // Avoid deopting on observable arguments or heterogeneous types.
if (
typeof observedBits !== 'number' ||
observedBits === MAX_SIGNED_31_BIT_INT
) {
// Observe all updates.
lastContextWithAllBitsObserved = ((context: any): ReactContext<mixed>);
resolvedObservedBits = MAX_SIGNED_31_BIT_INT;
} else {
resolvedObservedBits = observedBits;
}
let contextItem = {
context: ((context: any): ReactContext<mixed>),
observedBits: resolvedObservedBits,
next: null,
};
if (lastContextDependency === null) {
invariant(
currentlyRenderingFiber !== null,
'Context can only be read while React is ' +
'rendering, e.g. inside the render method or getDerivedStateFromProps.',
);
// This is the first dependency in the list
currentlyRenderingFiber.firstContextDependency = lastContextDependency = contextItem;
} else {
// Append a new context item.
lastContextDependency = lastContextDependency.next = contextItem;
}
}
return isPrimaryRenderer ? context._currentValue : context._currentValue2;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment