Created
December 12, 2018 07:54
-
-
Save Jokcy/c6b0e36383dbfc0e0e254f2ffe416a6b to your computer and use it in GitHub Desktop.
react next context readContext and ...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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