[Fiber] Remove array indirection in host context (#8544)
* Remove array indirection in host context * Keep a single context stack with a null sentinel This lets us keep subtrees separated without maintaining independent context arrays for subtrees. * There is always exactly one null by the time we pop a portal I was trying to be smart but didn't need to. * Cache current context
Dan Abramov committed
Dec 12, 2016 at 15:20 UTC
0f34bdc622cc79835b599d2f1d8b0889b138e579
1 file changed
+68
-61
src/renderers/shared/fiber/ReactFiberHostContext.js
+68
-61
@@ -34,113 +34,120 @@ module.exports = function<T, P, I, TI, C, CX>(
34
getChildHostContext,
35
} = config;
36
37
- type ContainerState = {
38
- rootInstance : C,
39
- contextFibers : Array<Fiber | null> | null,
40
- contextValues : Array<CX | null> | null,
41
- contextDepth: number
42
- };
43
-
44
- function createContainerState(rootInstance) {
45
- return {
46
- rootInstance,
47
- contextFibers: null,
48
- contextValues: null,
49
- contextDepth: -1,
50
- };
51
- }
52
-
53
- // If we meet any portals, we'll go deeper.
54
- let containerStack : Array<ContainerState> = [];
55
- let containerDepth : number = -1;
37
+ // Context stack is reused across the subtrees.
38
+ // We use a null sentinel on the fiber stack to separate them.
39
+ let contextFibers : Array<Fiber | null> | null = null;
40
+ let contextValues : Array<CX | null> | null = null;
41
+ let contextDepth : number = -1;
42
+ // Current context for fast access.
43
+ let currentContextValue : CX | null = null;
44
+ // Current root instance for fast access.
45
+ let rootInstance : C | null = null;
46
+ // A stack of outer root instances if we're in a portal.
47
+ let portalStack : Array<C | null> = [];
48
+ let portalDepth : number = -1;
49
50
function getRootHostContainer() : C {
58
- if (containerDepth === -1) {
59
- throw new Error('Expected to find a root container.');
51
+ if (rootInstance == null) {
52
+ throw new Error('Expected root container to exist.');
53
}
61
- const containerState = containerStack[containerDepth];
62
- const {rootInstance} = containerState;
54
return rootInstance;
55
}
56
66
- function pushHostContainer(portalHostContainer) {
67
- containerDepth++;
68
- if (containerDepth === containerStack.length) {
69
- containerStack[containerDepth] = createContainerState(portalHostContainer);
57
+ function pushHostContainer(nextRootInstance : C) {
58
+ if (rootInstance == null) {
59
+ // We're entering a root.
60
+ rootInstance = nextRootInstance;
61
} else {
71
- const containerState = containerStack[containerDepth];
72
- containerState.rootInstance = portalHostContainer;
73
- containerState.contextFibers = null;
74
- containerState.contextValues = null;
75
- containerState.contextDepth = -1;
62
+ // We're entering a portal.
63
+ // Save the current root to the portal stack.
64
+ portalDepth++;
65
+ portalStack[portalDepth] = rootInstance;
66
+ rootInstance = nextRootInstance;
67
+ // Delimit subtree context with a sentinel so we know where to pop later.
68
+ if (contextFibers != null && contextValues != null) {
69
+ contextDepth++;
70
+ contextFibers[contextDepth] = null;
71
+ contextValues[contextDepth] = null;
72
+ currentContextValue = null;
73
+ }
74
}
75
}
76
77
function popHostContainer() {
80
- if (containerDepth === -1) {
81
- throw new Error('Already reached the root.');
78
+ if (portalDepth === -1) {
79
+ // We're popping the root.
80
+ rootInstance = null;
81
+ currentContextValue = null;
82
+ contextDepth = -1;
83
+ } else {
84
+ // We're popping a portal.
85
+ // Restore the root instance.
86
+ rootInstance = portalStack[portalDepth];
87
+ portalStack[portalDepth] = null;
88
+ portalDepth--;
89
+ // If we pushed any context while in a portal, we need to roll it back.
90
+ if (contextDepth > -1) {
91
+ contextDepth--;
92
+ if (contextDepth > -1 && contextValues != null) {
93
+ currentContextValue = contextValues[contextDepth];
94
+ } else {
95
+ currentContextValue = null;
96
+ }
97
+ }
98
}
83
- containerDepth--;
99
}
100
101
function getHostContext() : CX | null {
87
- if (containerDepth == null) {
88
- throw new Error('Expected to find a root container.');
89
- }
90
- const containerState = containerStack[containerDepth];
91
- const {contextDepth, contextValues} = containerState;
92
- if (contextDepth === -1) {
93
- return null;
94
- }
95
- if (contextValues == null) {
96
- throw new Error('Expected context values to exist.');
97
- }
98
- return contextValues[contextDepth];
102
+ return currentContextValue;
103
}
104
105
function pushHostContext(fiber : Fiber) : void {
102
- const parentHostContext = getHostContext();
103
- const currentHostContext = getChildHostContext(parentHostContext, fiber.type);
104
- if (parentHostContext === currentHostContext) {
106
+ const nextContextValue = getChildHostContext(currentContextValue, fiber.type);
107
+ if (currentContextValue === nextContextValue) {
108
return;
109
}
107
- const containerState = containerStack[containerDepth];
108
- let {contextDepth, contextFibers, contextValues} = containerState;
110
if (contextFibers == null) {
111
contextFibers = [];
111
- containerState.contextFibers = contextFibers;
112
}
113
if (contextValues == null) {
114
contextValues = [];
115
- containerState.contextValues = contextValues;
115
}
116
contextDepth++;
118
- containerState.contextDepth = contextDepth;
117
contextFibers[contextDepth] = fiber;
120
- contextValues[contextDepth] = currentHostContext;
118
+ contextValues[contextDepth] = nextContextValue;
119
+ currentContextValue = nextContextValue;
120
}
121
122
function popHostContext(fiber : Fiber) : void {
124
- const containerState = containerStack[containerDepth];
125
- let {contextDepth} = containerState;
123
if (contextDepth === -1) {
124
return;
125
}
129
- const {contextFibers, contextValues} = containerState;
126
if (contextFibers == null || contextValues == null) {
127
throw new Error('Expected host context stacks to exist when index is more than -1.');
128
}
129
if (fiber !== contextFibers[contextDepth]) {
130
return;
131
}
132
+
133
contextFibers[contextDepth] = null;
134
contextValues[contextDepth] = null;
135
contextDepth--;
139
- containerState.contextDepth = contextDepth;
136
+ if (contextDepth > -1) {
137
+ currentContextValue = contextValues[contextDepth];
138
+ } else {
139
+ currentContextValue = null;
140
+ }
141
}
142
143
function resetHostContainer() {
143
- containerDepth = -1;
144
+ // Reset portal stack pointer because we're starting from the very top.
145
+ portalDepth = -1;
146
+ // Reset current container state.
147
+ // Don't reset arrays because we reuse them.
148
+ rootInstance = null;
149
+ contextDepth = -1;
150
+ currentContextValue = null;
151
}
152
153
return {