Lazily initialize dependencies object (#15944)

Most fibers do not have events or context, so we save memory lazily
initializing this container node.

Follow-up from #15927
This commit is contained in:
Andrew Clark
2019-06-20 20:12:22 -07:00
committed by GitHub
parent 720db4cbe6
commit 175111de72
4 changed files with 65 additions and 43 deletions
+14 -12
View File
@@ -709,8 +709,7 @@ function getTargetEventResponderInstances(
while (node !== null) {
// Traverse up the fiber tree till we find event component fibers.
const tag = node.tag;
const events = node.dependencies.events;
const dependencies = node.dependencies;
if (tag === EventComponent) {
const eventComponentInstance = node.stateNode;
// Switch to the current fiber tree
@@ -721,16 +720,19 @@ function getTargetEventResponderInstances(
eventResponderInstances,
eventComponentResponders,
);
} else if (tag === FunctionComponent && events !== null) {
for (let i = 0; i < events.length; i++) {
const eventComponentInstance = events[i];
if (eventComponentResponders.has(eventComponentInstance.responder)) {
storeTargetEventResponderInstance(
listeningName,
eventComponentInstance,
eventResponderInstances,
null,
);
} else if (tag === FunctionComponent && dependencies !== null) {
const events = dependencies.events;
if (events !== null) {
for (let i = 0; i < events.length; i++) {
const eventComponentInstance = events[i];
if (eventComponentResponders.has(eventComponentInstance.responder)) {
storeTargetEventResponderInstance(
listeningName,
eventComponentInstance,
eventResponderInstances,
null,
);
}
}
}
}
+15 -8
View File
@@ -169,8 +169,8 @@ export type Fiber = {|
// The state used to create the output
memoizedState: any,
// An object of dependencies for this fiber
dependencies: Dependencies,
// Dependencies (contexts, events) for this fiber, if it has any
dependencies: Dependencies | null,
// Bitfield that describes properties about the fiber and its subtree. E.g.
// the ConcurrentMode flag indicates whether the subtree should be async-by-
@@ -270,11 +270,7 @@ function FiberNode(
this.memoizedProps = null;
this.updateQueue = null;
this.memoizedState = null;
this.dependencies = {
expirationTime: 0,
firstContext: null,
events: null,
};
this.dependencies = null;
this.mode = mode;
@@ -443,7 +439,18 @@ export function createWorkInProgress(
workInProgress.memoizedProps = current.memoizedProps;
workInProgress.memoizedState = current.memoizedState;
workInProgress.updateQueue = current.updateQueue;
workInProgress.dependencies = current.dependencies;
// Clone the dependencies object. This is mutated during the render phase, so
// it cannot be shared with the current fiber.
const currentDependencies = current.dependencies;
workInProgress.dependencies =
currentDependencies === null
? null
: {
expirationTime: currentDependencies.expirationTime,
firstContext: currentDependencies.firstContext,
events: currentDependencies.events,
};
// These will be overridden during the parent's reconciliation
workInProgress.sibling = current.sibling;
+17 -5
View File
@@ -7,7 +7,7 @@
* @flow
*/
import type {Fiber} from './ReactFiber';
import type {Fiber, Dependencies} from './ReactFiber';
import type {ReactEventComponentInstance} from 'shared/ReactTypes';
import type {EventResponder} from 'react-reconciler/src/ReactFiberHostConfig';
@@ -18,6 +18,7 @@ import {
SuspenseComponent,
Fragment,
} from 'shared/ReactWorkTags';
import {NoWork} from './ReactFiberExpirationTime';
import invariant from 'shared/invariant';
let currentlyRenderingFiber: null | Fiber = null;
@@ -37,10 +38,21 @@ export function updateEventComponentInstance(
'The "%s" event responder cannot be used via the "useEvent" hook.',
responder.displayName,
);
const dependencies = ((currentlyRenderingFiber: any): Fiber).dependencies;
let events = dependencies.events;
if (events === null) {
dependencies.events = events = [];
let events;
let dependencies: Dependencies | null = ((currentlyRenderingFiber: any): Fiber)
.dependencies;
if (dependencies === null) {
events = [];
dependencies = ((currentlyRenderingFiber: any): Fiber).dependencies = {
expirationTime: NoWork,
firstContext: null,
events,
};
} else {
events = dependencies.events;
if (events === null) {
dependencies.events = events = [];
}
}
if (currentEventComponentInstanceIndex === events.length) {
let responderState = null;
+19 -18
View File
@@ -196,12 +196,11 @@ export function propagateContextChange(
let nextFiber;
// Visit this fiber.
const dependencies = fiber.dependencies;
const list = dependencies.firstContext;
const list = fiber.dependencies;
if (list !== null) {
nextFiber = fiber.child;
let dependency = list;
let dependency = list.firstContext;
while (dependency !== null) {
// Check if the context matches.
if (
@@ -235,8 +234,8 @@ export function propagateContextChange(
scheduleWorkOnParentPath(fiber.return, renderExpirationTime);
// Mark the expiration time on the list, too.
if (dependencies.expirationTime < renderExpirationTime) {
dependencies.expirationTime = renderExpirationTime;
if (list.expirationTime < renderExpirationTime) {
list.expirationTime = renderExpirationTime;
}
// Since we already found a match, we can stop traversing the
@@ -312,17 +311,17 @@ export function prepareToReadContext(
lastContextWithAllBitsObserved = null;
const dependencies = workInProgress.dependencies;
const firstContext = dependencies.firstContext;
if (
firstContext !== null &&
dependencies.expirationTime >= renderExpirationTime
) {
// Context list has a pending update. Mark that this fiber performed work.
markWorkInProgressReceivedUpdate();
if (dependencies !== null) {
const firstContext = dependencies.firstContext;
if (firstContext !== null) {
if (dependencies.expirationTime >= renderExpirationTime) {
// Context list has a pending update. Mark that this fiber performed work.
markWorkInProgressReceivedUpdate();
}
// Reset the work-in-progress list
dependencies.firstContext = null;
}
}
// Reset the work-in-progress list
dependencies.firstContext = null;
}
export function readContext<T>(
@@ -375,9 +374,11 @@ export function readContext<T>(
// This is the first dependency for this component. Create a new list.
lastContextDependency = contextItem;
const dependencies = currentlyRenderingFiber.dependencies;
dependencies.expirationTime = NoWork;
dependencies.firstContext = contextItem;
currentlyRenderingFiber.dependencies = {
expirationTime: NoWork,
firstContext: contextItem,
events: null,
};
} else {
// Append a new context item.
lastContextDependency = lastContextDependency.next = contextItem;