7
* @flow
8
*/
9
10
+import type {ThreadID} from './ReactThreadIDAllocator';
11
import type {ReactElement} from 'shared/ReactElementType';
12
import type {ReactProvider, ReactContext} from 'shared/ReactTypes';
13
17
import lowPriorityWarning from 'shared/lowPriorityWarning';
18
import warning from 'shared/warning';
19
import warningWithoutStack from 'shared/warningWithoutStack';
19
-import checkPropTypes from 'prop-types/checkPropTypes';
20
import describeComponentFrame from 'shared/describeComponentFrame';
21
import ReactSharedInternals from 'shared/ReactSharedInternals';
22
import {
39
REACT_MEMO_TYPE,
40
} from 'shared/ReactSymbols';
41
42
+import {
43
+ emptyObject,
44
+ processContext,
45
+ validateContextBounds,
46
+} from './ReactPartialRendererContext';
47
+import {allocThreadID, freeThreadID} from './ReactThreadIDAllocator';
48
import {
49
createMarkupForCustomAttribute,
50
createMarkupForProperty,
56
finishHooks,
57
Dispatcher,
58
DispatcherWithoutHooks,
59
+ currentThreadID,
60
+ setCurrentThreadID,
61
} from './ReactPartialRendererHooks';
62
import {
63
Namespaces,
184
const didWarnAboutDeprecatedWillMount = {};
185
const didWarnAboutUndefinedDerivedState = {};
186
const didWarnAboutUninitializedState = {};
179
-const didWarnAboutInvalidateContextType = {};
187
const valuePropNames = ['value', 'defaultValue'];
188
const newlineEatingTags = {
189
listing: true,
331
return content;
332
}
333
327
-const emptyObject = {};
328
-if (__DEV__) {
329
- Object.freeze(emptyObject);
330
-}
331
-
332
-function maskContext(type, context) {
333
- const contextTypes = type.contextTypes;
334
- if (!contextTypes) {
335
- return emptyObject;
336
- }
337
- const maskedContext = {};
338
- for (const contextName in contextTypes) {
339
- maskedContext[contextName] = context[contextName];
340
- }
341
- return maskedContext;
342
-}
343
-
344
-function checkContextTypes(typeSpecs, values, location: string) {
345
- if (__DEV__) {
346
- checkPropTypes(
347
- typeSpecs,
348
- values,
349
- location,
350
- 'Component',
351
- getCurrentServerStackImpl,
352
- );
353
- }
354
-}
355
-
356
-function processContext(type, context) {
357
- const contextType = type.contextType;
358
- if (typeof contextType === 'object' && contextType !== null) {
359
- if (__DEV__) {
360
- if (contextType.$$typeof !== REACT_CONTEXT_TYPE) {
361
- let name = getComponentName(type) || 'Component';
362
- if (!didWarnAboutInvalidateContextType[name]) {
363
- didWarnAboutInvalidateContextType[type] = true;
364
- warningWithoutStack(
365
- false,
366
- '%s defines an invalid contextType. ' +
367
- 'contextType should point to the Context object returned by React.createContext(). ' +
368
- 'Did you accidentally pass the Context.Provider instead?',
369
- name,
370
- );
371
- }
372
- }
373
- }
374
- return contextType._currentValue;
375
- } else {
376
- const maskedContext = maskContext(type, context);
377
- if (__DEV__) {
378
- if (type.contextTypes) {
379
- checkContextTypes(type.contextTypes, maskedContext, 'context');
380
- }
381
- }
382
- return maskedContext;
383
- }
384
-}
385
-
334
const hasOwnProperty = Object.prototype.hasOwnProperty;
335
const STYLE = 'style';
336
const RESERVED_PROPS = {
401
function resolve(
402
child: mixed,
403
context: Object,
404
+ threadID: ThreadID,
405
): {|
406
child: mixed,
407
context: Object,
421
422
// Extra closure so queue and replace can be captured properly
423
function processChild(element, Component) {
475
- let publicContext = processContext(Component, context);
424
+ let publicContext = processContext(Component, context, threadID);
425
426
let queue = [];
427
let replace = false;
667
};
668
669
class ReactDOMServerRenderer {
670
+ threadID: ThreadID;
671
stack: Array<Frame>;
672
exhausted: boolean;
673
// TODO: type this more strictly:
697
if (__DEV__) {
698
((topFrame: any): FrameDev).debugElementStack = [];
699
}
700
+ this.threadID = allocThreadID();
701
this.stack = [topFrame];
702
this.exhausted = false;
703
this.currentSelectValue = null;
714
}
715
}
716
717
+ destroy() {
718
+ if (!this.exhausted) {
719
+ this.exhausted = true;
720
+ freeThreadID(this.threadID);
721
+ }
722
+ }
723
+
724
/**
725
* Note: We use just two stacks regardless of how many context providers you have.
726
* Providers are always popped in the reverse order to how they were pushed
734
pushProvider<T>(provider: ReactProvider<T>): void {
735
const index = ++this.contextIndex;
736
const context: ReactContext<any> = provider.type._context;
779
- const previousValue = context._currentValue;
737
+ const threadID = this.threadID;
738
+ validateContextBounds(context, threadID);
739
+ const previousValue = context[threadID];
740
741
// Remember which value to restore this context to on our way up.
742
this.contextStack[index] = context;
747
}
748
749
// Mutate the current value.
790
- context._currentValue = provider.props.value;
750
+ context[threadID] = provider.props.value;
751
}
752
753
popProvider<T>(provider: ReactProvider<T>): void {
773
this.contextIndex--;
774
775
// Restore to the previous value we stored as we were walking down.
816
- context._currentValue = previousValue;
776
+ // We've already verified that this context has been expanded to accommodate
777
+ // this thread id, so we don't need to do it again.
778
+ context[this.threadID] = previousValue;
779
}
780
781
read(bytes: number): string | null {
783
return null;
784
}
785
786
+ const prevThreadID = currentThreadID;
787
+ setCurrentThreadID(this.threadID);
788
const prevDispatcher = ReactCurrentOwner.currentDispatcher;
789
if (enableHooks) {
790
ReactCurrentOwner.currentDispatcher = Dispatcher;
799
while (out[0].length < bytes) {
800
if (this.stack.length === 0) {
801
this.exhausted = true;
802
+ freeThreadID(this.threadID);
803
break;
804
}
805
const frame: Frame = this.stack[this.stack.length - 1];
871
return out[0];
872
} finally {
873
ReactCurrentOwner.currentDispatcher = prevDispatcher;
874
+ setCurrentThreadID(prevThreadID);
875
}
876
}
877
895
return escapeTextForBrowser(text);
896
} else {
897
let nextChild;
932
- ({child: nextChild, context} = resolve(child, context));
898
+ ({child: nextChild, context} = resolve(child, context, this.threadID));
899
if (nextChild === null || nextChild === false) {
900
return '';
901
} else if (!React.isValidElement(nextChild)) {
1102
}
1103
}
1104
const nextProps: any = (nextChild: any).props;
1139
- const nextValue = reactContext._currentValue;
1105
+ const threadID = this.threadID;
1106
+ validateContextBounds(reactContext, threadID);
1107
+ const nextValue = reactContext[threadID];
1108
1109
const nextChildren = toArray(nextProps.children(nextValue));
1110
const frame: Frame = {