Added better context support
Brian Vaughn committed
Feb 5, 2019 at 08:32 UTC
d0d5b677de1b5566021463fed95adf8be1bca56d
7 files changed
+201
-56
package.json
+1
-1
@@ -84,7 +84,7 @@
84
"nullthrows": "^1.0.0",
85
"object-assign": "4.0.1",
86
"prettier": "^1.16.1",
87
- "prop-types": "^15.6.1",
87
+ "prop-types": "^15.6.2",
88
"react": "^16.8.0-alpha.1",
89
"react-color": "^2.11.7",
90
"react-dom": "^16.8.0-alpha.1",
shells/dev/app/InspectableElements/Contexts.js
new
+116
@@ -0,0 +1,116 @@
1
+// @flow
2
+
3
+import React, { createContext, Component, Fragment, useContext } from 'react';
4
+import PropTypes from 'prop-types';
5
+
6
+function someNamedFunction() {}
7
+
8
+const contextData = {
9
+ array: ['first', 'second', 'third'],
10
+ bool: true,
11
+ func: someNamedFunction,
12
+ number: 123,
13
+ object: { outer: { inner: {} } },
14
+ string: 'abc',
15
+ symbol: Symbol.for('symbol'),
16
+};
17
+
18
+class LegacyContextProvider extends Component<any> {
19
+ static childContextTypes = {
20
+ array: PropTypes.array,
21
+ bool: PropTypes.bool,
22
+ func: PropTypes.func,
23
+ number: PropTypes.number,
24
+ object: PropTypes.object,
25
+ string: PropTypes.string,
26
+ symbol: PropTypes.symbol,
27
+ };
28
+
29
+ getChildContext() {
30
+ return contextData;
31
+ }
32
+
33
+ render() {
34
+ return this.props.children;
35
+ }
36
+}
37
+
38
+class LegacyContextConsumer extends Component<any> {
39
+ static contextTypes = {
40
+ array: PropTypes.array,
41
+ bool: PropTypes.bool,
42
+ func: PropTypes.func,
43
+ number: PropTypes.number,
44
+ object: PropTypes.object,
45
+ string: PropTypes.string,
46
+ symbol: PropTypes.symbol,
47
+ };
48
+
49
+ render() {
50
+ return null;
51
+ }
52
+}
53
+
54
+const ModernContext = createContext();
55
+// $FlowFixMe Flow does not yet know about Context.displayName
56
+ModernContext.displayName = 'ModernContext';
57
+const ArrayContext = createContext(contextData.array);
58
+// $FlowFixMe Flow does not yet know about Context.displayName
59
+ArrayContext.displayName = 'ArrayContext';
60
+const BoolContext = createContext(contextData.bool);
61
+// $FlowFixMe Flow does not yet know about Context.displayName
62
+BoolContext.displayName = 'BoolContext';
63
+const FuncContext = createContext(contextData.func);
64
+// $FlowFixMe Flow does not yet know about Context.displayName
65
+FuncContext.displayName = 'FuncContext';
66
+const NumberContext = createContext(contextData.number);
67
+// $FlowFixMe Flow does not yet know about Context.displayName
68
+NumberContext.displayName = 'NumberContext';
69
+const StringContext = createContext(contextData.string);
70
+// $FlowFixMe Flow does not yet know about Context.displayName
71
+StringContext.displayName = 'StringContext';
72
+const SymbolContext = createContext(contextData.symbol);
73
+// $FlowFixMe Flow does not yet know about Context.displayName
74
+SymbolContext.displayName = 'SymbolContext';
75
+const NullContext = createContext(null);
76
+// $FlowFixMe Flow does not yet know about Context.displayName
77
+NullContext.displayName = 'NullContext';
78
+const UndefinedContext = createContext(undefined);
79
+// $FlowFixMe Flow does not yet know about Context.displayName
80
+UndefinedContext.displayName = 'UndefinedContext';
81
+
82
+class ModernContextType extends Component<any> {
83
+ static contextType = ModernContext;
84
+
85
+ render() {
86
+ return null;
87
+ }
88
+}
89
+
90
+function FunctionalContextConsumer() {
91
+ const string = useContext(StringContext);
92
+ return string;
93
+}
94
+
95
+export default function Contexts() {
96
+ return (
97
+ <Fragment>
98
+ <LegacyContextProvider>
99
+ <LegacyContextConsumer />
100
+ </LegacyContextProvider>
101
+ <ModernContext.Provider value={contextData}>
102
+ <ModernContext.Consumer>{value => null}</ModernContext.Consumer>
103
+ <ModernContextType />
104
+ </ModernContext.Provider>
105
+ <FunctionalContextConsumer />
106
+ <ArrayContext.Consumer>{value => null}</ArrayContext.Consumer>
107
+ <BoolContext.Consumer>{value => null}</BoolContext.Consumer>
108
+ <FuncContext.Consumer>{value => null}</FuncContext.Consumer>
109
+ <NumberContext.Consumer>{value => null}</NumberContext.Consumer>
110
+ <StringContext.Consumer>{value => null}</StringContext.Consumer>
111
+ <SymbolContext.Consumer>{value => null}</SymbolContext.Consumer>
112
+ <NullContext.Consumer>{value => null}</NullContext.Consumer>
113
+ <UndefinedContext.Consumer>{value => null}</UndefinedContext.Consumer>
114
+ </Fragment>
115
+ );
116
+}
shells/dev/app/InspectableElements/InspectableElements.js
+2
@@ -1,6 +1,7 @@
1
// @flow
2
3
import React, { Fragment } from 'react';
4
+import Contexts from './Contexts';
5
import FunctionWithState from './FunctionWithState';
6
import NestedProps from './NestedProps';
7
@@ -9,6 +10,7 @@ export default function InspectableElements() {
10
<Fragment>
11
<FunctionWithState initialCount={1} />
12
<NestedProps />
13
+ <Contexts />
14
</Fragment>
15
);
16
}
src/backend/renderer.js
+72
-27
@@ -219,7 +219,7 @@ export function attach(
219
220
// Keep this function in sync with getDataForFiber()
221
function shouldFilterFiber(fiber: Fiber): boolean {
222
- const { type, tag } = fiber;
222
+ const { tag } = fiber;
223
224
switch (tag) {
225
case ClassComponent:
@@ -237,16 +237,9 @@ export function attach(
237
case Fragment:
238
return true;
239
default:
240
- const symbolOrNumber =
241
- typeof type === 'object' && type !== null ? type.$$typeof : type;
240
+ const typeSymbol = getTypeSymbol(fiber);
241
243
- const switchValue =
244
- // $FlowFixMe facebook/flow/issues/2362
245
- typeof symbolOrNumber === 'symbol'
246
- ? symbolOrNumber.toString()
247
- : symbolOrNumber;
248
-
249
- switch (switchValue) {
242
+ switch (typeSymbol) {
243
case CONCURRENT_MODE_NUMBER:
244
case CONCURRENT_MODE_SYMBOL_STRING:
245
case DEPRECATED_ASYNC_MODE_SYMBOL_STRING:
@@ -269,6 +262,18 @@ export function attach(
262
}
263
}
264
265
+ function getTypeSymbol(fiber: Fiber): Symbol | number {
266
+ const { type } = fiber;
267
+
268
+ const symbolOrNumber =
269
+ typeof type === 'object' && type !== null ? type.$$typeof : type;
270
+
271
+ // $FlowFixMe facebook/flow/issues/2362
272
+ return typeof symbolOrNumber === 'symbol'
273
+ ? symbolOrNumber.toString()
274
+ : symbolOrNumber;
275
+ }
276
+
277
// TODO: we might want to change the data structure once we no longer suppport Stack versions of `getData`.
278
// TODO: Keep in sync with getElementType()
279
function getDataForFiber(fiber: Fiber): FiberData {
@@ -340,16 +345,9 @@ export function attach(
345
};
346
break;
347
default:
343
- const symbolOrNumber =
344
- typeof type === 'object' && type !== null ? type.$$typeof : type;
345
-
346
- const switchValue =
347
- // $FlowFixMe facebook/flow/issues/2362
348
- typeof symbolOrNumber === 'symbol'
349
- ? symbolOrNumber.toString()
350
- : symbolOrNumber;
348
+ const typeSymbol = getTypeSymbol(fiber);
349
352
- switch (switchValue) {
350
+ switch (typeSymbol) {
351
case CONCURRENT_MODE_NUMBER:
352
case CONCURRENT_MODE_SYMBOL_STRING:
353
case DEPRECATED_ASYNC_MODE_SYMBOL_STRING:
@@ -362,6 +360,7 @@ export function attach(
360
case CONTEXT_PROVIDER_SYMBOL_STRING:
361
// 16.3.0 exposed the context object as "context"
362
// PR #12501 changed it to "_context" for 16.3.1+
363
+ // NOTE Keep in sync with inspectElement()
364
resolvedContext = fiber.type._context || fiber.type.context;
365
displayName = `${resolvedContext.displayName ||
366
'Context'}.Provider`;
@@ -376,6 +375,7 @@ export function attach(
375
case CONTEXT_CONSUMER_SYMBOL_STRING:
376
// 16.3-16.5 read from "type" because the Consumer is the actual context object.
377
// 16.6+ should read from "type._context" because Consumer can be different (in DEV).
378
+ // NOTE Keep in sync with inspectElement()
379
resolvedContext = fiber.type._context || fiber.type;
380
381
// NOTE: TraceUpdatesBackendManager depends on the name ending in '.Consumer'
@@ -1019,6 +1019,7 @@ export function attach(
1019
memoizedProps,
1020
memoizedState,
1021
tag,
1022
+ type,
1023
} = ((fiber: any): Fiber);
1024
1025
const usesHooks =
@@ -1027,11 +1028,60 @@ export function attach(
1028
tag === ForwardRef) &&
1029
!!memoizedState;
1030
1030
- const hasContext =
1031
+ const typeSymbol = getTypeSymbol(fiber);
1032
+
1033
+ let context = null;
1034
+ if (
1035
tag === ClassComponent ||
1036
tag === FunctionComponent ||
1037
tag === IncompleteClassComponent ||
1034
- tag === IndeterminateComponent;
1038
+ tag === IndeterminateComponent
1039
+ ) {
1040
+ if (stateNode && Object.keys(stateNode.context).length > 0) {
1041
+ context = stateNode.context;
1042
+ }
1043
+ } else if (
1044
+ typeSymbol === CONTEXT_CONSUMER_NUMBER ||
1045
+ typeSymbol === CONTEXT_CONSUMER_SYMBOL_STRING
1046
+ ) {
1047
+ // 16.3-16.5 read from "type" because the Consumer is the actual context object.
1048
+ // 16.6+ should read from "type._context" because Consumer can be different (in DEV).
1049
+ // NOTE Keep in sync with getDataForFiber()
1050
+ const consumerResolvedContext = type._context || type;
1051
+
1052
+ // Global context value.
1053
+ context = consumerResolvedContext._currentValue || null;
1054
+
1055
+ // Look for overridden value.
1056
+ let current = ((fiber: any): Fiber).return;
1057
+ while (current !== null) {
1058
+ const currentTypeSymbol = getTypeSymbol(current);
1059
+ if (
1060
+ currentTypeSymbol === CONTEXT_PROVIDER_NUMBER ||
1061
+ currentTypeSymbol === CONTEXT_PROVIDER_SYMBOL_STRING
1062
+ ) {
1063
+ const currentType = current.type;
1064
+
1065
+ // 16.3.0 exposed the context object as "context"
1066
+ // PR #12501 changed it to "_context" for 16.3.1+
1067
+ // NOTE Keep in sync with getDataForFiber()
1068
+ const providerResolvedContext =
1069
+ currentType._context || currentType.context;
1070
+ if (providerResolvedContext === consumerResolvedContext) {
1071
+ context = current.memoizedProps.value;
1072
+ break;
1073
+ }
1074
+ }
1075
+
1076
+ current = current.return;
1077
+ }
1078
+ }
1079
+
1080
+ if (context !== null) {
1081
+ // To simplify hydration and display logic for context, wrap in a value object.
1082
+ // Otherwise simple values (e.g. strings, booleans) become harder to handle.
1083
+ context = cleanForBridge({ value: context });
1084
+ }
1085
1086
let owners = null;
1087
if (_debugOwner) {
@@ -1054,12 +1104,7 @@ export function attach(
1104
1105
// Inspectable properties.
1106
// TODO Sanitize props, state, and context
1057
- context:
1058
- (hasContext &&
1059
- stateNode &&
1060
- Object.keys(stateNode.context).length > 0 &&
1061
- cleanForBridge(stateNode.context)) ||
1062
- null,
1107
+ context,
1108
hooks: usesHooks
1109
? cleanForBridge(
1110
inspectHooksOfFiber(fiber, (renderer.currentDispatcherRef: any))
src/devtools/views/InspectedElementTree.js
+8
-3
@@ -43,6 +43,13 @@ function KeyValue({ depth, name, value }: KeyValueProps) {
43
44
let children = null;
45
if (isSimpleType) {
46
+ let displayValue = value;
47
+ if (dataType === 'string') {
48
+ displayValue = `"${value}"`;
49
+ } else if (dataType === 'boolean') {
50
+ displayValue = value ? 'true' : 'false';
51
+ }
52
+
53
children = (
54
<div
55
key="root"
@@ -50,9 +57,7 @@ function KeyValue({ depth, name, value }: KeyValueProps) {
57
style={{ paddingLeft: `${depth}rem` }}
58
>
59
<span className={styles.Name}>{name}</span>:{' '}
53
- <span className={styles.Value}>
54
- {dataType === 'string' ? `"${value}"` : value}
55
- </span>
60
+ <span className={styles.Value}>{displayValue}</span>
61
</div>
62
);
63
} else if (value.hasOwnProperty(meta.type)) {
src/devtools/views/SelectedElement.js
+1
-1
@@ -94,7 +94,7 @@ function InspectedElementView({
94
95
{owners !== null && owners.length > 0 && (
96
<div className={styles.Owners}>
97
- <div>owners</div>
97
+ <div>owner stack</div>
98
{owners.map(owner => (
99
<div
100
key={owner.id}
yarn.lock
+1
-24
@@ -4400,18 +4400,6 @@ fbjs@0.5.1:
4400
ua-parser-js "^0.7.9"
4401
whatwg-fetch "^0.9.0"
4402
4403
-fbjs@^0.8.16:
4404
- version "0.8.17"
4405
- resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
4406
- dependencies:
4407
- core-js "^1.0.0"
4408
- isomorphic-fetch "^2.1.1"
4409
- loose-envify "^1.0.0"
4410
- object-assign "^4.1.0"
4411
- promise "^7.1.1"
4412
- setimmediate "^1.0.5"
4413
- ua-parser-js "^0.7.18"
4414
-
4403
fbjs@^0.8.9:
4404
version "0.8.12"
4405
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.12.tgz#10b5d92f76d45575fd63a217d4ea02bea2f8ed04"
@@ -8217,17 +8205,10 @@ prop-types@^15.5.4, prop-types@^15.5.8:
8205
fbjs "^0.8.9"
8206
loose-envify "^1.3.1"
8207
8220
-prop-types@^15.6.1:
8221
- version "15.6.1"
8222
- resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca"
8223
- dependencies:
8224
- fbjs "^0.8.16"
8225
- loose-envify "^1.3.1"
8226
- object-assign "^4.1.1"
8227
-
8208
prop-types@^15.6.2:
8209
version "15.6.2"
8210
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102"
8211
+ integrity sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==
8212
dependencies:
8213
loose-envify "^1.3.1"
8214
object-assign "^4.1.1"
@@ -10006,10 +9987,6 @@ typedarray@^0.0.6:
9987
version "0.0.6"
9988
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
9989
10009
-ua-parser-js@^0.7.18:
10010
- version "0.7.18"
10011
- resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.18.tgz#a7bfd92f56edfb117083b69e31d2aa8882d4b1ed"
10012
-
9990
ua-parser-js@^0.7.9:
9991
version "0.7.12"
9992
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.12.tgz#04c81a99bdd5dc52263ea29d24c6bf8d4818a4bb"