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

Devtools/function context change (#22047)

Byron Luk committed Aug 10, 2021 at 11:16 UTC da627ded867c882290776b4477250b6b312b77b4
4 files changed +396 -62
packages/react-devtools-shared/src/__tests__/__snapshots__/profilingCache-test.js.snap
+180 -31
@@ -2485,7 +2485,7 @@ Object {
2485 exports[`ProfilingCache should properly detect changed hooks: CommitDetails commitIndex: 0 1`] = `
2486 Object {
2487 "changeDescriptions": Map {
2488 - 3 => Object {
2488 + 4 => Object {
2489 "context": null,
2490 "didHooksChange": false,
2491 "isFirstMount": true,
@@ -2499,11 +2499,13 @@ Object {
2499 1 => 0,
2500 2 => 0,
2501 3 => 0,
2502 + 4 => 0,
2503 },
2504 "fiberSelfDurations": Map {
2505 1 => 0,
2506 2 => 0,
2507 3 => 0,
2508 + 4 => 0,
2509 },
2510 "passiveEffectDuration": 0,
2511 "priorityLevel": "Immediate",
@@ -2523,8 +2525,8 @@ Object {
2525 exports[`ProfilingCache should properly detect changed hooks: CommitDetails commitIndex: 1 1`] = `
2526 Object {
2527 "changeDescriptions": Map {
2526 - 3 => Object {
2527 - "context": null,
2528 + 4 => Object {
2529 + "context": false,
2530 "didHooksChange": false,
2531 "isFirstMount": false,
2532 "props": Array [
@@ -2536,11 +2538,13 @@ Object {
2538 "duration": 0,
2539 "effectDuration": null,
2540 "fiberActualDurations": Map {
2541 + 4 => 0,
2542 3 => 0,
2543 2 => 0,
2544 1 => 0,
2545 },
2546 "fiberSelfDurations": Map {
2547 + 4 => 0,
2548 3 => 0,
2549 2 => 0,
2550 1 => 0,
@@ -2563,8 +2567,8 @@ Object {
2567 exports[`ProfilingCache should properly detect changed hooks: CommitDetails commitIndex: 2 1`] = `
2568 Object {
2569 "changeDescriptions": Map {
2566 - 3 => Object {
2567 - "context": null,
2570 + 4 => Object {
2571 + "context": false,
2572 "didHooksChange": true,
2573 "isFirstMount": false,
2574 "props": Array [],
@@ -2574,10 +2578,10 @@ Object {
2578 "duration": 0,
2579 "effectDuration": null,
2580 "fiberActualDurations": Map {
2577 - 3 => 0,
2581 + 4 => 0,
2582 },
2583 "fiberSelfDurations": Map {
2580 - 3 => 0,
2584 + 4 => 0,
2585 },
2586 "passiveEffectDuration": null,
2587 "priorityLevel": "Immediate",
@@ -2586,7 +2590,7 @@ Object {
2590 Object {
2591 "displayName": "Component",
2592 "hocDisplayNames": null,
2589 - "id": 3,
2593 + "id": 4,
2594 "key": null,
2595 "type": 5,
2596 },
@@ -2597,8 +2601,8 @@ Object {
2601 exports[`ProfilingCache should properly detect changed hooks: CommitDetails commitIndex: 3 1`] = `
2602 Object {
2603 "changeDescriptions": Map {
2600 - 3 => Object {
2601 - "context": null,
2604 + 4 => Object {
2605 + "context": false,
2606 "didHooksChange": true,
2607 "isFirstMount": false,
2608 "props": Array [],
@@ -2608,10 +2612,10 @@ Object {
2612 "duration": 0,
2613 "effectDuration": null,
2614 "fiberActualDurations": Map {
2611 - 3 => 0,
2615 + 4 => 0,
2616 },
2617 "fiberSelfDurations": Map {
2614 - 3 => 0,
2618 + 4 => 0,
2619 },
2620 "passiveEffectDuration": null,
2621 "priorityLevel": "Immediate",
@@ -2620,7 +2624,7 @@ Object {
2624 Object {
2625 "displayName": "Component",
2626 "hocDisplayNames": null,
2623 - "id": 3,
2627 + "id": 4,
2628 "key": null,
2629 "type": 5,
2630 },
@@ -2631,8 +2635,48 @@ Object {
2635 exports[`ProfilingCache should properly detect changed hooks: CommitDetails commitIndex: 4 1`] = `
2636 Object {
2637 "changeDescriptions": Map {
2634 - 3 => Object {
2635 - "context": null,
2638 + 4 => Object {
2639 + "context": true,
2640 + "didHooksChange": false,
2641 + "isFirstMount": false,
2642 + "props": Array [],
2643 + "state": null,
2644 + },
2645 + },
2646 + "duration": 0,
2647 + "effectDuration": null,
2648 + "fiberActualDurations": Map {
2649 + 4 => 0,
2650 + 3 => 0,
2651 + 2 => 0,
2652 + 1 => 0,
2653 + },
2654 + "fiberSelfDurations": Map {
2655 + 4 => 0,
2656 + 3 => 0,
2657 + 2 => 0,
2658 + 1 => 0,
2659 + },
2660 + "passiveEffectDuration": null,
2661 + "priorityLevel": "Immediate",
2662 + "timestamp": 0,
2663 + "updaters": Array [
2664 + Object {
2665 + "displayName": "Anonymous",
2666 + "hocDisplayNames": null,
2667 + "id": 1,
2668 + "key": null,
2669 + "type": 11,
2670 + },
2671 + ],
2672 +}
2673 +`;
2674 +
2675 +exports[`ProfilingCache should properly detect changed hooks: CommitDetails commitIndex: 5 1`] = `
2676 +Object {
2677 + "changeDescriptions": Map {
2678 + 4 => Object {
2679 + "context": true,
2680 "didHooksChange": false,
2681 "isFirstMount": false,
2682 "props": Array [],
@@ -2642,11 +2686,13 @@ Object {
2686 "duration": 0,
2687 "effectDuration": null,
2688 "fiberActualDurations": Map {
2689 + 4 => 0,
2690 3 => 0,
2691 2 => 0,
2692 1 => 0,
2693 },
2694 "fiberSelfDurations": Map {
2695 + 4 => 0,
2696 3 => 0,
2697 2 => 0,
2698 1 => 0,
@@ -2674,7 +2720,7 @@ Object {
2720 Object {
2721 "changeDescriptions": Array [
2722 Array [
2677 - 3,
2723 + 4,
2724 Object {
2725 "context": null,
2726 "didHooksChange": false,
@@ -2699,6 +2745,10 @@ Object {
2745 3,
2746 0,
2747 ],
2748 + Array [
2749 + 4,
2750 + 0,
2751 + ],
2752 ],
2753 "fiberSelfDurations": Array [
2754 Array [
@@ -2713,6 +2763,10 @@ Object {
2763 3,
2764 0,
2765 ],
2766 + Array [
2767 + 4,
2768 + 0,
2769 + ],
2770 ],
2771 "passiveEffectDuration": 0,
2772 "priorityLevel": "Immediate",
@@ -2730,9 +2784,9 @@ Object {
2784 Object {
2785 "changeDescriptions": Array [
2786 Array [
2733 - 3,
2787 + 4,
2788 Object {
2735 - "context": null,
2789 + "context": false,
2790 "didHooksChange": false,
2791 "isFirstMount": false,
2792 "props": Array [
@@ -2745,6 +2799,10 @@ Object {
2799 "duration": 0,
2800 "effectDuration": null,
2801 "fiberActualDurations": Array [
2802 + Array [
2803 + 4,
2804 + 0,
2805 + ],
2806 Array [
2807 3,
2808 0,
@@ -2759,6 +2817,10 @@ Object {
2817 ],
2818 ],
2819 "fiberSelfDurations": Array [
2820 + Array [
2821 + 4,
2822 + 0,
2823 + ],
2824 Array [
2825 3,
2826 0,
@@ -2788,9 +2850,9 @@ Object {
2850 Object {
2851 "changeDescriptions": Array [
2852 Array [
2791 - 3,
2853 + 4,
2854 Object {
2793 - "context": null,
2855 + "context": false,
2856 "didHooksChange": true,
2857 "isFirstMount": false,
2858 "props": Array [],
@@ -2802,13 +2864,13 @@ Object {
2864 "effectDuration": null,
2865 "fiberActualDurations": Array [
2866 Array [
2805 - 3,
2867 + 4,
2868 0,
2869 ],
2870 ],
2871 "fiberSelfDurations": Array [
2872 Array [
2811 - 3,
2873 + 4,
2874 0,
2875 ],
2876 ],
@@ -2819,7 +2881,7 @@ Object {
2881 Object {
2882 "displayName": "Component",
2883 "hocDisplayNames": null,
2822 - "id": 3,
2884 + "id": 4,
2885 "key": null,
2886 "type": 5,
2887 },
@@ -2828,9 +2890,9 @@ Object {
2890 Object {
2891 "changeDescriptions": Array [
2892 Array [
2831 - 3,
2893 + 4,
2894 Object {
2833 - "context": null,
2895 + "context": false,
2896 "didHooksChange": true,
2897 "isFirstMount": false,
2898 "props": Array [],
@@ -2842,13 +2904,13 @@ Object {
2904 "effectDuration": null,
2905 "fiberActualDurations": Array [
2906 Array [
2845 - 3,
2907 + 4,
2908 0,
2909 ],
2910 ],
2911 "fiberSelfDurations": Array [
2912 Array [
2851 - 3,
2913 + 4,
2914 0,
2915 ],
2916 ],
@@ -2859,7 +2921,7 @@ Object {
2921 Object {
2922 "displayName": "Component",
2923 "hocDisplayNames": null,
2862 - "id": 3,
2924 + "id": 4,
2925 "key": null,
2926 "type": 5,
2927 },
@@ -2867,10 +2929,74 @@ Object {
2929 },
2930 Object {
2931 "changeDescriptions": Array [
2932 + Array [
2933 + 4,
2934 + Object {
2935 + "context": true,
2936 + "didHooksChange": false,
2937 + "isFirstMount": false,
2938 + "props": Array [],
2939 + "state": null,
2940 + },
2941 + ],
2942 + ],
2943 + "duration": 0,
2944 + "effectDuration": null,
2945 + "fiberActualDurations": Array [
2946 + Array [
2947 + 4,
2948 + 0,
2949 + ],
2950 + Array [
2951 + 3,
2952 + 0,
2953 + ],
2954 + Array [
2955 + 2,
2956 + 0,
2957 + ],
2958 + Array [
2959 + 1,
2960 + 0,
2961 + ],
2962 + ],
2963 + "fiberSelfDurations": Array [
2964 + Array [
2965 + 4,
2966 + 0,
2967 + ],
2968 Array [
2969 3,
2970 + 0,
2971 + ],
2972 + Array [
2973 + 2,
2974 + 0,
2975 + ],
2976 + Array [
2977 + 1,
2978 + 0,
2979 + ],
2980 + ],
2981 + "passiveEffectDuration": null,
2982 + "priorityLevel": "Immediate",
2983 + "timestamp": 0,
2984 + "updaters": Array [
2985 + Object {
2986 + "displayName": "Anonymous",
2987 + "hocDisplayNames": null,
2988 + "id": 1,
2989 + "key": null,
2990 + "type": 11,
2991 + },
2992 + ],
2993 + },
2994 + Object {
2995 + "changeDescriptions": Array [
2996 + Array [
2997 + 4,
2998 Object {
2873 - "context": null,
2999 + "context": true,
3000 "didHooksChange": false,
3001 "isFirstMount": false,
3002 "props": Array [],
@@ -2881,6 +3007,10 @@ Object {
3007 "duration": 0,
3008 "effectDuration": null,
3009 "fiberActualDurations": Array [
3010 + Array [
3011 + 4,
3012 + 0,
3013 + ],
3014 Array [
3015 3,
3016 0,
@@ -2895,6 +3025,10 @@ Object {
3025 ],
3026 ],
3027 "fiberSelfDurations": Array [
3028 + Array [
3029 + 4,
3030 + 0,
3031 + ],
3032 Array [
3033 3,
3034 0,
@@ -2973,14 +3107,29 @@ Object {
3107 0,
3108 1,
3109 3,
2976 - 5,
3110 2,
2978 - 0,
3111 2,
3112 0,
3113 + 1,
3114 + 0,
3115 + 4,
3116 + 3,
3117 + 0,
3118 + 1,
3119 4,
3120 + 5,
3121 3,
3122 0,
3123 + 2,
3124 + 0,
3125 + 4,
3126 + 4,
3127 + 0,
3128 + ],
3129 + Array [
3130 + 1,
3131 + 1,
3132 + 0,
3133 ],
3134 Array [
3135 1,
packages/react-devtools-shared/src/__tests__/profilingCache-test.js
+26 -8
@@ -311,6 +311,7 @@ describe('ProfilingCache', () => {
311
312 it('should properly detect changed hooks', () => {
313 const Context = React.createContext(0);
314 + const Context2 = React.createContext(0);
315
316 function reducer(state, action) {
317 switch (action.type) {
@@ -332,6 +333,7 @@ describe('ProfilingCache', () => {
333 // This hook's return value may change between renders,
334 // but the hook itself isn't stateful.
335 React.useContext(Context);
336 + React.useContext(Context2);
337
338 // These hooks and their dependencies may not change between renders.
339 // We're using them to ensure that they don't trigger false positives.
@@ -351,7 +353,9 @@ describe('ProfilingCache', () => {
353 utils.act(() =>
354 legacyRender(
355 <Context.Provider value={true}>
354 - <Component count={1} />
356 + <Context2.Provider value={true}>
357 + <Component count={1} />
358 + </Context2.Provider>
359 </Context.Provider>,
360 container,
361 ),
@@ -361,7 +365,9 @@ describe('ProfilingCache', () => {
365 utils.act(() =>
366 legacyRender(
367 <Context.Provider value={true}>
364 - <Component count={2} />
368 + <Context2.Provider value={true}>
369 + <Component count={2} />
370 + </Context2.Provider>
371 </Context.Provider>,
372 container,
373 ),
@@ -373,17 +379,29 @@ describe('ProfilingCache', () => {
379 // Fourth render has a changed state hook
380 utils.act(() => setState('def'));
381
376 - // Fifth render has a changed context value, but no changed hook.
377 - // Technically, DevTools will miss this "context" change since it only tracks legacy context.
382 + // Fifth render has a changed context value for context 1, but no changed hook.
383 utils.act(() =>
384 legacyRender(
385 <Context.Provider value={false}>
381 - <Component count={2} />
386 + <Context2.Provider value={true}>
387 + <Component count={2} />
388 + </Context2.Provider>
389 </Context.Provider>,
390 container,
391 ),
392 );
393
394 + // Sixth render has another changed context value for context 2, but no changed hook.
395 + utils.act(() =>
396 + legacyRender(
397 + <Context.Provider value={false}>
398 + <Context2.Provider value={false}>
399 + <Component count={2} />
400 + </Context2.Provider>
401 + </Context.Provider>,
402 + container,
403 + ),
404 + );
405 utils.act(() => store.profilerStore.stopProfiling());
406
407 const allCommitData = [];
@@ -403,7 +421,7 @@ describe('ProfilingCache', () => {
421
422 const rootID = store.roots[0];
423
406 - for (let commitIndex = 0; commitIndex < 5; commitIndex++) {
424 + for (let commitIndex = 0; commitIndex < 6; commitIndex++) {
425 utils.act(() => {
426 TestRenderer.create(
427 <Validator
@@ -415,12 +433,12 @@ describe('ProfilingCache', () => {
433 });
434 }
435
418 - expect(allCommitData).toHaveLength(5);
436 + expect(allCommitData).toHaveLength(6);
437
438 // Export and re-import profile data and make sure it is retained.
439 utils.exportImportHelper(bridge, store);
440
423 - for (let commitIndex = 0; commitIndex < 5; commitIndex++) {
441 + for (let commitIndex = 0; commitIndex < 6; commitIndex++) {
442 utils.act(() => {
443 TestRenderer.create(
444 <Validator
packages/react-devtools-shared/src/backend/renderer.js
+54 -22
@@ -960,6 +960,7 @@ export function attach(
960
961 return false;
962 }
963 +
964 // NOTICE Keep in sync with shouldFilterFiber() and other get*ForFiber methods
965 function getElementTypeForFiber(fiber: Fiber): ElementType {
966 const {type, tag} = fiber;
@@ -1236,6 +1237,7 @@ export function attach(
1237
1238 function updateContextsForFiber(fiber: Fiber) {
1239 switch (getElementTypeForFiber(fiber)) {
1240 + case ElementTypeFunction:
1241 case ElementTypeClass:
1242 if (idToContextsMap !== null) {
1243 const id = getFiberIDThrows(fiber);
@@ -1254,11 +1256,12 @@ export function attach(
1256 const NO_CONTEXT = {};
1257
1258 function getContextsForFiber(fiber: Fiber): [Object, any] | null {
1259 + let legacyContext = NO_CONTEXT;
1260 + let modernContext = NO_CONTEXT;
1261 +
1262 switch (getElementTypeForFiber(fiber)) {
1263 case ElementTypeClass:
1264 const instance = fiber.stateNode;
1260 - let legacyContext = NO_CONTEXT;
1261 - let modernContext = NO_CONTEXT;
1265 if (instance != null) {
1266 if (
1267 instance.constructor &&
@@ -1272,6 +1275,13 @@ export function attach(
1275 }
1276 }
1277 }
1278 + return [legacyContext, modernContext];
1279 + case ElementTypeFunction:
1280 + const dependencies = fiber.dependencies;
1281 + if (dependencies && dependencies.firstContext) {
1282 + modernContext = dependencies.firstContext;
1283 + }
1284 +
1285 return [legacyContext, modernContext];
1286 default:
1287 return null;
@@ -1291,31 +1301,50 @@ export function attach(
1301 }
1302
1303 function getContextChangedKeys(fiber: Fiber): null | boolean | Array<string> {
1294 - switch (getElementTypeForFiber(fiber)) {
1295 - case ElementTypeClass:
1296 - if (idToContextsMap !== null) {
1297 - const id = getFiberIDThrows(fiber);
1298 - const prevContexts = idToContextsMap.has(id)
1299 - ? idToContextsMap.get(id)
1300 - : null;
1301 - const nextContexts = getContextsForFiber(fiber);
1304 + if (idToContextsMap !== null) {
1305 + const id = getFiberIDThrows(fiber);
1306 + const prevContexts = idToContextsMap.has(id)
1307 + ? idToContextsMap.get(id)
1308 + : null;
1309 + const nextContexts = getContextsForFiber(fiber);
1310
1303 - if (prevContexts == null || nextContexts == null) {
1304 - return null;
1311 + if (prevContexts == null || nextContexts == null) {
1312 + return null;
1313 + }
1314 +
1315 + const [prevLegacyContext, prevModernContext] = prevContexts;
1316 + const [nextLegacyContext, nextModernContext] = nextContexts;
1317 +
1318 + switch (getElementTypeForFiber(fiber)) {
1319 + case ElementTypeClass:
1320 + if (prevContexts && nextContexts) {
1321 + if (nextLegacyContext !== NO_CONTEXT) {
1322 + return getChangedKeys(prevLegacyContext, nextLegacyContext);
1323 + } else if (nextModernContext !== NO_CONTEXT) {
1324 + return prevModernContext !== nextModernContext;
1325 + }
1326 }
1327 + break;
1328 + case ElementTypeFunction:
1329 + if (nextModernContext !== NO_CONTEXT) {
1330 + let prevContext = prevModernContext;
1331 + let nextContext = nextModernContext;
1332 +
1333 + while (prevContext && nextContext) {
1334 + if (!is(prevContext.memoizedValue, nextContext.memoizedValue)) {
1335 + return true;
1336 + }
1337
1307 - const [prevLegacyContext, prevModernContext] = prevContexts;
1308 - const [nextLegacyContext, nextModernContext] = nextContexts;
1338 + prevContext = prevContext.next;
1339 + nextContext = nextContext.next;
1340 + }
1341
1310 - if (nextLegacyContext !== NO_CONTEXT) {
1311 - return getChangedKeys(prevLegacyContext, nextLegacyContext);
1312 - } else if (nextModernContext !== NO_CONTEXT) {
1313 - return prevModernContext !== nextModernContext;
1342 + return false;
1343 }
1315 - }
1316 - break;
1317 - default:
1318 - break;
1344 + break;
1345 + default:
1346 + break;
1347 + }
1348 }
1349 return null;
1350 }
@@ -2859,6 +2888,7 @@ export function attach(
2888 // Otherwise B has to be current branch.
2889 return alternate;
2890 }
2891 +
2892 // END copied code
2893
2894 function prepareViewAttributeSource(
@@ -3860,6 +3890,7 @@ export function attach(
3890 // Map of id and its force error status: true (error), false (toggled off),
3891 // null (do nothing)
3892 const forceErrorForFiberIDs = new Map();
3893 +
3894 function shouldErrorFiberAccordingToMap(fiber) {
3895 if (typeof setErrorHandler !== 'function') {
3896 throw new Error(
@@ -3924,6 +3955,7 @@ export function attach(
3955 }
3956
3957 const forceFallbackForSuspenseIDs = new Set();
3958 +
3959 function shouldSuspendFiberAccordingToSet(fiber) {
3960 const maybeID = getFiberIDUnsafe(((fiber: any): Fiber));
3961 return maybeID !== null && forceFallbackForSuspenseIDs.has(maybeID);
packages/react-devtools-shell/src/app/InspectableElements/Contexts.js
+136 -1
@@ -8,7 +8,7 @@
8 */
9
10 import * as React from 'react';
11 -import {createContext, Component, useContext} from 'react';
11 +import {createContext, Component, useContext, useState} from 'react';
12 import PropTypes from 'prop-types';
13
14 function someNamedFunction() {}
@@ -73,6 +73,43 @@ class LegacyContextConsumer extends Component<any> {
73 }
74 }
75
76 +class LegacyContextProviderWithUpdates extends Component<any> {
77 + constructor(props) {
78 + super(props);
79 + this.state = {type: 'desktop'};
80 + }
81 +
82 + getChildContext() {
83 + return {type: this.state.type};
84 + }
85 +
86 + handleChange = event => {
87 + this.setState({type: event.target.value});
88 + };
89 +
90 + render() {
91 + return (
92 + <>
93 + <LegacyFunctionalContextConsumer />
94 + <div>
95 + <input value={this.state.type} onChange={this.handleChange} />
96 + </div>
97 + </>
98 + );
99 + }
100 +}
101 +
102 +LegacyContextProviderWithUpdates.childContextTypes = {
103 + type: PropTypes.string,
104 +};
105 +
106 +function LegacyFunctionalContextConsumer(props, context) {
107 + return formatContextForDisplay('LegacyFunctionContextConsumer', context.type);
108 +}
109 +LegacyFunctionalContextConsumer.contextTypes = {
110 + type: PropTypes.string,
111 +};
112 +
113 const ModernContext = createContext();
114 ModernContext.displayName = 'ModernContext';
115 const ArrayContext = createContext(contextData.array);
@@ -105,6 +142,101 @@ function FunctionalContextConsumer() {
142 return formatContextForDisplay('FunctionalContextConsumer', value);
143 }
144
145 +const StringContextWithUpdates = createContext({
146 + string: contextData.string,
147 + setString: (string: string) => {},
148 +});
149 +const StringContextWithUpdates2 = createContext({
150 + string2: contextData.string,
151 + setString2: (string: string) => {},
152 +});
153 +
154 +function FunctionalContextProviderWithContextUpdates() {
155 + const [string, setString] = useState(contextData.string);
156 + const [string2, setString2] = useState(contextData.string);
157 + const value = {string, setString};
158 + const value2 = {string2, setString2};
159 +
160 + return (
161 + <StringContextWithUpdates.Provider value={value}>
162 + <StringContextWithUpdates2.Provider value={value2}>
163 + <FunctionalContextConsumerWithContextUpdates />
164 + </StringContextWithUpdates2.Provider>
165 + </StringContextWithUpdates.Provider>
166 + );
167 +}
168 +
169 +function FunctionalContextConsumerWithContextUpdates() {
170 + const {string, setString} = useContext(StringContextWithUpdates);
171 + const {string2, setString2} = useContext(StringContextWithUpdates2);
172 + const [state, setState] = useState('state');
173 +
174 + const handleChange = e => setString(e.target.value);
175 + const handleChange2 = e => setString2(e.target.value);
176 +
177 + return (
178 + <>
179 + {formatContextForDisplay(
180 + 'FunctionalContextConsumerWithUpdates',
181 + `context: ${string}, context 2: ${string2}`,
182 + )}
183 + <div>
184 + context: <input value={string} onChange={handleChange} />
185 + </div>
186 + <div>
187 + context 2: <input value={string2} onChange={handleChange2} />
188 + </div>
189 + <div>
190 + {state}
191 + <div>
192 + test state:{' '}
193 + <input value={state} onChange={e => setState(e.target.value)} />
194 + </div>
195 + </div>
196 + </>
197 + );
198 +}
199 +
200 +class ModernClassContextProviderWithUpdates extends Component<any> {
201 + constructor(props) {
202 + super(props);
203 + this.setString = string => {
204 + this.setState({string});
205 + };
206 +
207 + this.state = {
208 + string: contextData.string,
209 + setString: this.setString,
210 + };
211 + }
212 +
213 + render() {
214 + return (
215 + <StringContextWithUpdates.Provider value={this.state}>
216 + <ModernClassContextConsumerWithUpdates />
217 + </StringContextWithUpdates.Provider>
218 + );
219 + }
220 +}
221 +
222 +class ModernClassContextConsumerWithUpdates extends Component<any> {
223 + render() {
224 + return (
225 + <StringContextWithUpdates.Consumer>
226 + {({string, setString}) => (
227 + <>
228 + {formatContextForDisplay(
229 + 'ModernClassContextConsumerWithUpdates',
230 + string,
231 + )}
232 + <input value={string} onChange={e => setString(e.target.value)} />
233 + </>
234 + )}
235 + </StringContextWithUpdates.Consumer>
236 + );
237 + }
238 +}
239 +
240 export default function Contexts() {
241 return (
242 <div>
@@ -113,6 +245,7 @@ export default function Contexts() {
245 <LegacyContextProvider>
246 <LegacyContextConsumer />
247 </LegacyContextProvider>
248 + <LegacyContextProviderWithUpdates />
249 <ModernContext.Provider value={contextData}>
250 <ModernContext.Consumer>
251 {value => formatContextForDisplay('ModernContext.Consumer', value)}
@@ -120,6 +253,8 @@ export default function Contexts() {
253 <ModernContextType />
254 </ModernContext.Provider>
255 <FunctionalContextConsumer />
256 + <FunctionalContextProviderWithContextUpdates />
257 + <ModernClassContextProviderWithUpdates />
258 <ArrayContext.Consumer>
259 {value => formatContextForDisplay('ArrayContext.Consumer', value)}
260 </ArrayContext.Consumer>