@samitouri / QOS-React-2 / commits / d0d5b677de

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"