35
var checkReactTypeSpec = require('checkReactTypeSpec');
36
}
37
38
-let contextStackCursor : StackCursor<?Object> = createCursor((null: ?Object));
38
+// A cursor to the current merged context object on the stack.
39
+let contextStackCursor : StackCursor<Object> = createCursor(emptyObject);
40
+// A cursor to a boolean indicating whether the context has changed.
41
let didPerformWorkStackCursor : StackCursor<boolean> = createCursor(false);
40
-
41
-function getUnmaskedContext() {
42
- return contextStackCursor.current || emptyObject;
42
+// Keep track of the previous context object that was on the stack.
43
+// We use this to get access to the parent context after we have already
44
+// pushed the next context provider, and now need to merge their contexts.
45
+let previousContext : Object = emptyObject;
46
+
47
+function getUnmaskedContext(workInProgress : Fiber) : Object {
48
+ const hasOwnContext = isContextProvider(workInProgress);
49
+ if (hasOwnContext) {
50
+ // If the fiber is a context provider itself, when we read its context
51
+ // we have already pushed its own child context on the stack. A context
52
+ // provider should not "see" its own child context. Therefore we read the
53
+ // previous (parent) context instead for a context provider.
54
+ return previousContext;
55
+ }
56
+ return contextStackCursor.current;
57
}
58
59
exports.getMaskedContext = function(workInProgress : Fiber) {
63
return emptyObject;
64
}
65
52
- const unmaskedContext = getUnmaskedContext();
66
+ const unmaskedContext = getUnmaskedContext(workInProgress);
67
const context = {};
54
-
68
for (let key in contextTypes) {
69
context[key] = unmaskedContext[key];
70
}
84
function isContextProvider(fiber : Fiber) : boolean {
85
return (
86
fiber.tag === ClassComponent &&
74
- // Instance might be null, if the fiber errored during construction
75
- fiber.stateNode &&
76
- typeof fiber.stateNode.getChildContext === 'function'
87
+ fiber.type.childContextTypes != null
88
);
89
}
90
exports.isContextProvider = isContextProvider;
91
92
function popContextProvider(fiber : Fiber) : void {
93
+ if (!isContextProvider(fiber)) {
94
+ return;
95
+ }
96
+
97
pop(didPerformWorkStackCursor, fiber);
98
pop(contextStackCursor, fiber);
99
}
132
}
133
exports.processChildContext = processChildContext;
134
120
-exports.pushContextProvider = function(workInProgress : Fiber, didPerformWork : boolean) : void {
121
- const instance = workInProgress.stateNode;
122
- const memoizedMergedChildContext = instance.__reactInternalMemoizedMergedChildContext;
123
- const canReuseMergedChildContext = !didPerformWork && memoizedMergedChildContext != null;
124
-
125
- let mergedContext = null;
126
- if (canReuseMergedChildContext) {
127
- mergedContext = memoizedMergedChildContext;
128
- } else {
129
- mergedContext = processChildContext(workInProgress, getUnmaskedContext(), true);
130
- instance.__reactInternalMemoizedMergedChildContext = mergedContext;
135
+exports.pushContextProvider = function(workInProgress : Fiber) : boolean {
136
+ if (!isContextProvider(workInProgress)) {
137
+ return false;
138
}
139
140
+ const instance = workInProgress.stateNode;
141
+ // We push the context as early as possible to ensure stack integrity.
142
+ // If the instance does not exist yet, we will push null at first,
143
+ // and replace it on the stack later when invalidating the context.
144
+ const memoizedMergedChildContext = (
145
+ instance &&
146
+ instance.__reactInternalMemoizedMergedChildContext
147
+ ) || emptyObject;
148
+
149
+ // Remember the parent context so we can merge with it later.
150
+ previousContext = contextStackCursor.current;
151
+ push(contextStackCursor, memoizedMergedChildContext, workInProgress);
152
+ push(didPerformWorkStackCursor, false, workInProgress);
153
+
154
+ return true;
155
+};
156
+
157
+exports.invalidateContextProvider = function(workInProgress : Fiber) : void {
158
+ const instance = workInProgress.stateNode;
159
+ invariant(instance, 'Expected to have an instance by this point.');
160
+
161
+ // Merge parent and own context.
162
+ const mergedContext = processChildContext(workInProgress, previousContext, true);
163
+ instance.__reactInternalMemoizedMergedChildContext = mergedContext;
164
+
165
+ // Replace the old (or empty) context with the new one.
166
+ // It is important to unwind the context in the reverse order.
167
+ pop(didPerformWorkStackCursor, workInProgress);
168
+ pop(contextStackCursor, workInProgress);
169
+ // Now push the new context and mark that it has changed.
170
push(contextStackCursor, mergedContext, workInProgress);
134
- push(didPerformWorkStackCursor, didPerformWork, workInProgress);
171
+ push(didPerformWorkStackCursor, true, workInProgress);
172
};
173
174
exports.resetContext = function() : void {
138
- contextStackCursor.current = null;
175
+ previousContext = emptyObject;
176
+ contextStackCursor.current = emptyObject;
177
didPerformWorkStackCursor.current = false;
178
};
179