/
githubmr
/
facebook-react
Обзор
Документация
Войти
/
githubmr
/
facebook-react
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
main
packages/react-reconciler/src/ReactFiberLegacyContext.js
324 строки
10 KB
Jan Kassens
rename ReactFiberContext to ReactFiberLegacyContext (#33622)
24 июн 2025, 00:21
Не верифицирован
24 июн 2025, 00:21
aab72cb
Код
Авторство
О чём код?
/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @flow */ import type {Fiber} from './ReactInternalTypes'; import type {StackCursor} from './ReactFiberStack'; import {disableLegacyContext} from 'shared/ReactFeatureFlags'; import {ClassComponent, HostRoot} from './ReactWorkTags'; import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber'; import {createCursor, push, pop} from './ReactFiberStack'; let warnedAboutMissingGetChildContext; if (__DEV__) { warnedAboutMissingGetChildContext = ({}: {[string]: boolean}); } export const emptyContextObject: {} = {}; if (__DEV__) { Object.freeze(emptyContextObject); } // A cursor to the current merged context object on the stack. const contextStackCursor: StackCursor<Object> = createCursor(emptyContextObject); // A cursor to a boolean indicating whether the context has changed. const didPerformWorkStackCursor: StackCursor<boolean> = createCursor(false); // Keep track of the previous context object that was on the stack. // We use this to get access to the parent context after we have already // pushed the next context provider, and now need to merge their contexts. let previousContext: Object = emptyContextObject; function getUnmaskedContext( workInProgress: Fiber, Component: Function, didPushOwnContextIfProvider: boolean, ): Object { if (disableLegacyContext) { return emptyContextObject; } else { if (didPushOwnContextIfProvider && isContextProvider(Component)) { // If the fiber is a context provider itself, when we read its context // we may have already pushed its own child context on the stack. A context // provider should not "see" its own child context. Therefore we read the // previous (parent) context instead for a context provider. return previousContext; } return contextStackCursor.current; } } function cacheContext( workInProgress: Fiber, unmaskedContext: Object, maskedContext: Object, ): void { if (disableLegacyContext) { return; } else { const instance = workInProgress.stateNode; instance.__reactInternalMemoizedUnmaskedChildContext = unmaskedContext; instance.__reactInternalMemoizedMaskedChildContext = maskedContext; } } function getMaskedContext( workInProgress: Fiber, unmaskedContext: Object, ): Object { if (disableLegacyContext) { return emptyContextObject; } else { const type = workInProgress.type; const contextTypes = type.contextTypes; if (!contextTypes) { return emptyContextObject; } // Avoid recreating masked context unless unmasked context has changed. // Failing to do this will result in unnecessary calls to componentWillReceiveProps. // This may trigger infinite loops if componentWillReceiveProps calls setState. const instance = workInProgress.stateNode; if ( instance && instance.__reactInternalMemoizedUnmaskedChildContext === unmaskedContext ) { return instance.__reactInternalMemoizedMaskedChildContext; } const context: {[string]: $FlowFixMe} = {}; for (const key in contextTypes) { context[key] = unmaskedContext[key]; } // Cache unmasked context so we can avoid recreating masked context unless necessary. // Context is created before the class component is instantiated so check for instance. if (instance) { cacheContext(workInProgress, unmaskedContext, context); } return context; } } function hasContextChanged(): boolean { if (disableLegacyContext) { return false; } else { return didPerformWorkStackCursor.current; } } function isContextProvider(type: Function): boolean { if (disableLegacyContext) { return false; } else { const childContextTypes = type.childContextTypes; return childContextTypes !== null && childContextTypes !== undefined; } } function popContext(fiber: Fiber): void { if (disableLegacyContext) { return; } else { pop(didPerformWorkStackCursor, fiber); pop(contextStackCursor, fiber); } } function popTopLevelContextObject(fiber: Fiber): void { if (disableLegacyContext) { return; } else { pop(didPerformWorkStackCursor, fiber); pop(contextStackCursor, fiber); } } function pushTopLevelContextObject( fiber: Fiber, context: Object, didChange: boolean, ): void { if (disableLegacyContext) { return; } else { if (contextStackCursor.current !== emptyContextObject) { throw new Error( 'Unexpected context found on stack. ' + 'This error is likely caused by a bug in React. Please file an issue.', ); } push(contextStackCursor, context, fiber); push(didPerformWorkStackCursor, didChange, fiber); } } function processChildContext( fiber: Fiber, type: any, parentContext: Object, ): Object { if (disableLegacyContext) { return parentContext; } else { const instance = fiber.stateNode; const childContextTypes = type.childContextTypes; // TODO (bvaughn) Replace this behavior with an invariant() in the future. // It has only been added in Fiber to match the (unintentional) behavior in Stack. if (typeof instance.getChildContext !== 'function') { if (__DEV__) { const componentName = getComponentNameFromFiber(fiber) || 'Unknown'; if (!warnedAboutMissingGetChildContext[componentName]) { warnedAboutMissingGetChildContext[componentName] = true; console.error( '%s.childContextTypes is specified but there is no getChildContext() method ' + 'on the instance. You can either define getChildContext() on %s or remove ' + 'childContextTypes from it.', componentName, componentName, ); } } return parentContext; } const childContext = instance.getChildContext(); for (const contextKey in childContext) { if (!(contextKey in childContextTypes)) { throw new Error( `${ getComponentNameFromFiber(fiber) || 'Unknown' }.getChildContext(): key "${contextKey}" is not defined in childContextTypes.`, ); } } return {...parentContext, ...childContext}; } } function pushContextProvider(workInProgress: Fiber): boolean { if (disableLegacyContext) { return false; } else { const instance = workInProgress.stateNode; // We push the context as early as possible to ensure stack integrity. // If the instance does not exist yet, we will push null at first, // and replace it on the stack later when invalidating the context. const memoizedMergedChildContext = (instance && instance.__reactInternalMemoizedMergedChildContext) || emptyContextObject; // Remember the parent context so we can merge with it later. // Inherit the parent's did-perform-work value to avoid inadvertently blocking updates. previousContext = contextStackCursor.current; push(contextStackCursor, memoizedMergedChildContext, workInProgress); push( didPerformWorkStackCursor, didPerformWorkStackCursor.current, workInProgress, ); return true; } } function invalidateContextProvider( workInProgress: Fiber, type: any, didChange: boolean, ): void { if (disableLegacyContext) { return; } else { const instance = workInProgress.stateNode; if (!instance) { throw new Error( 'Expected to have an instance by this point. ' + 'This error is likely caused by a bug in React. Please file an issue.', ); } if (didChange) { // Merge parent and own context. // Skip this if we're not updating due to sCU. // This avoids unnecessarily recomputing memoized values. const mergedContext = processChildContext( workInProgress, type, previousContext, ); instance.__reactInternalMemoizedMergedChildContext = mergedContext; // Replace the old (or empty) context with the new one. // It is important to unwind the context in the reverse order. pop(didPerformWorkStackCursor, workInProgress); pop(contextStackCursor, workInProgress); // Now push the new context and mark that it has changed. push(contextStackCursor, mergedContext, workInProgress); push(didPerformWorkStackCursor, didChange, workInProgress); } else { pop(didPerformWorkStackCursor, workInProgress); push(didPerformWorkStackCursor, didChange, workInProgress); } } } function findCurrentUnmaskedContext(fiber: Fiber): Object { if (disableLegacyContext) { return emptyContextObject; } else { // Currently this is only used with renderSubtreeIntoContainer; not sure if it // makes sense elsewhere let node: Fiber = fiber; do { switch (node.tag) { case HostRoot: return node.stateNode.context; case ClassComponent: { const Component = node.type; if (isContextProvider(Component)) { return node.stateNode.__reactInternalMemoizedMergedChildContext; } break; } } // $FlowFixMe[incompatible-type] we bail out when we get a null node = node.return; } while (node !== null); throw new Error( 'Found unexpected detached subtree parent. ' + 'This error is likely caused by a bug in React. Please file an issue.', ); } } export { getUnmaskedContext, cacheContext, getMaskedContext, hasContextChanged, popContext, popTopLevelContextObject, pushTopLevelContextObject, processChildContext, isContextProvider, pushContextProvider, invalidateContextProvider, findCurrentUnmaskedContext, };