@reckona/mreact-compat 0.0.90 → 0.0.92

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/README.md +1 -0
  2. package/dist/class-component.d.ts +20 -6
  3. package/dist/class-component.d.ts.map +1 -1
  4. package/dist/class-component.js +94 -51
  5. package/dist/class-component.js.map +1 -1
  6. package/dist/context.d.ts +19 -3
  7. package/dist/context.d.ts.map +1 -1
  8. package/dist/context.js +55 -6
  9. package/dist/context.js.map +1 -1
  10. package/dist/dom-children.d.ts +2 -0
  11. package/dist/dom-children.d.ts.map +1 -1
  12. package/dist/dom-children.js +103 -1
  13. package/dist/dom-children.js.map +1 -1
  14. package/dist/dom-host-rules.d.ts +10 -0
  15. package/dist/dom-host-rules.d.ts.map +1 -0
  16. package/dist/dom-host-rules.js +86 -0
  17. package/dist/dom-host-rules.js.map +1 -0
  18. package/dist/dom-props.d.ts +3 -2
  19. package/dist/dom-props.d.ts.map +1 -1
  20. package/dist/dom-props.js +229 -33
  21. package/dist/dom-props.js.map +1 -1
  22. package/dist/element.d.ts +9 -4
  23. package/dist/element.d.ts.map +1 -1
  24. package/dist/element.js +101 -26
  25. package/dist/element.js.map +1 -1
  26. package/dist/event-listeners.d.ts +4 -4
  27. package/dist/event-listeners.d.ts.map +1 -1
  28. package/dist/event-listeners.js +1 -1
  29. package/dist/event-listeners.js.map +1 -1
  30. package/dist/event-types.d.ts +10 -0
  31. package/dist/event-types.d.ts.map +1 -1
  32. package/dist/event-types.js.map +1 -1
  33. package/dist/events.js +22 -1
  34. package/dist/events.js.map +1 -1
  35. package/dist/fiber-commit.d.ts +2 -1
  36. package/dist/fiber-commit.d.ts.map +1 -1
  37. package/dist/fiber-commit.js +13 -1
  38. package/dist/fiber-commit.js.map +1 -1
  39. package/dist/fiber-reconciler.d.ts.map +1 -1
  40. package/dist/fiber-reconciler.js +28 -7
  41. package/dist/fiber-reconciler.js.map +1 -1
  42. package/dist/fiber-work-loop.d.ts.map +1 -1
  43. package/dist/fiber-work-loop.js +4 -3
  44. package/dist/fiber-work-loop.js.map +1 -1
  45. package/dist/fiber.d.ts +5 -0
  46. package/dist/fiber.d.ts.map +1 -1
  47. package/dist/fiber.js +9 -0
  48. package/dist/fiber.js.map +1 -1
  49. package/dist/hooks-entry.d.ts +3 -0
  50. package/dist/hooks-entry.d.ts.map +1 -0
  51. package/dist/hooks-entry.js +2 -0
  52. package/dist/hooks-entry.js.map +1 -0
  53. package/dist/hooks.d.ts +39 -5
  54. package/dist/hooks.d.ts.map +1 -1
  55. package/dist/hooks.js +373 -326
  56. package/dist/hooks.js.map +1 -1
  57. package/dist/host-reconciler.d.ts +3 -0
  58. package/dist/host-reconciler.d.ts.map +1 -1
  59. package/dist/host-reconciler.js +1152 -64
  60. package/dist/host-reconciler.js.map +1 -1
  61. package/dist/hydration.d.ts +1 -1
  62. package/dist/hydration.d.ts.map +1 -1
  63. package/dist/hydration.js.map +1 -1
  64. package/dist/index.d.ts +2 -1
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +2 -1
  67. package/dist/index.js.map +1 -1
  68. package/dist/react-default.d.ts +4 -4
  69. package/dist/react-default.d.ts.map +1 -1
  70. package/dist/react-default.js +2 -1
  71. package/dist/react-default.js.map +1 -1
  72. package/dist/reconciler.d.ts.map +1 -1
  73. package/dist/reconciler.js +38 -22
  74. package/dist/reconciler.js.map +1 -1
  75. package/dist/root.d.ts.map +1 -1
  76. package/dist/root.js +48 -13
  77. package/dist/root.js.map +1 -1
  78. package/dist/server-render.d.ts +6 -0
  79. package/dist/server-render.d.ts.map +1 -0
  80. package/dist/server-render.js +307 -0
  81. package/dist/server-render.js.map +1 -0
  82. package/package.json +6 -2
  83. package/src/class-component.ts +216 -51
  84. package/src/context.ts +108 -9
  85. package/src/dom-children.ts +155 -1
  86. package/src/dom-host-rules.ts +115 -0
  87. package/src/dom-props.ts +297 -46
  88. package/src/element.ts +141 -31
  89. package/src/event-listeners.ts +6 -6
  90. package/src/event-types.ts +10 -0
  91. package/src/events.ts +32 -10
  92. package/src/fiber-commit.ts +16 -1
  93. package/src/fiber-reconciler.ts +39 -6
  94. package/src/fiber-work-loop.ts +4 -3
  95. package/src/fiber.ts +14 -0
  96. package/src/hooks-entry.ts +24 -0
  97. package/src/hooks.ts +482 -479
  98. package/src/host-reconciler.ts +1628 -94
  99. package/src/hydration.ts +1 -1
  100. package/src/index.ts +1 -1
  101. package/src/react-default.ts +1 -1
  102. package/src/reconciler.ts +61 -22
  103. package/src/root.ts +55 -12
  104. package/src/server-render.ts +478 -0
@@ -5,6 +5,8 @@ import type { ReconcileNode, ReconcileResult } from "./reconcile-types.js";
5
5
  import { isThenable } from "./thenable.js";
6
6
  import { shallowEqual } from "./prop-comparison.js";
7
7
 
8
+ const CLASS_COMPONENT_RUNTIME_OWNER = Symbol.for("modular.react.class_component_runtime");
9
+
8
10
  export interface ClassComponentInstance {
9
11
  props: Record<string, unknown>;
10
12
  state?: Record<string, unknown>;
@@ -46,43 +48,144 @@ export interface ClassComponentType {
46
48
  getDerivedStateFromError?: (error: Error) => Record<string, unknown> | null;
47
49
  }
48
50
 
49
- export class Component<
51
+ export interface Component<
50
52
  P extends Record<string, unknown> = Record<string, unknown>,
51
53
  S extends Record<string, unknown> = Record<string, unknown>,
52
54
  > {
53
55
  props: P;
54
56
  state?: S;
55
- setState!: ClassComponentInstance["setState"];
56
- forceUpdate!: ClassComponentInstance["forceUpdate"];
57
-
58
- constructor(props: P) {
59
- this.props = props;
60
- }
57
+ setState(
58
+ partial:
59
+ | Partial<S>
60
+ | ((
61
+ previousState: Readonly<S>,
62
+ props: Readonly<P>,
63
+ ) => Partial<S> | S | null),
64
+ callback?: () => void,
65
+ ): void;
66
+ forceUpdate(callback?: () => void): void;
67
+ render(): ReactCompatNode;
68
+ }
61
69
 
62
- render(): ReactCompatNode {
63
- return null;
64
- }
70
+ export interface ComponentConstructor {
71
+ new <
72
+ P extends Record<string, unknown> = Record<string, unknown>,
73
+ S extends Record<string, unknown> = Record<string, unknown>,
74
+ >(props: P): Component<P, S>;
75
+ <
76
+ P extends Record<string, unknown> = Record<string, unknown>,
77
+ S extends Record<string, unknown> = Record<string, unknown>,
78
+ >(this: Component<P, S>, props: P): void;
79
+ prototype: Component<any, any>;
65
80
  }
66
81
 
67
- export class PureComponent<
82
+ export const Component: ComponentConstructor = function Component<
83
+ P extends Record<string, unknown> = Record<string, unknown>,
84
+ S extends Record<string, unknown> = Record<string, unknown>,
85
+ >(this: Component<P, S>, props: P): void {
86
+ this.props = props;
87
+ } as ComponentConstructor;
88
+
89
+ Component.prototype.setState = function setState<
90
+ P extends Record<string, unknown>,
91
+ S extends Record<string, unknown>,
92
+ >(
93
+ this: Component<P, S>,
94
+ partial:
95
+ | Partial<S>
96
+ | ((
97
+ previousState: Readonly<S>,
98
+ props: Readonly<P>,
99
+ ) => Partial<S> | S | null),
100
+ callback?: () => void,
101
+ ): void {
102
+ enqueueClassSetState(
103
+ this as unknown as ClassComponentInstance,
104
+ partial as Parameters<NonNullable<ClassComponentInstance["setState"]>>[0],
105
+ callback,
106
+ );
107
+ };
108
+
109
+ Component.prototype.forceUpdate = function forceUpdate(
110
+ this: Component,
111
+ callback?: () => void,
112
+ ): void {
113
+ enqueueClassForceUpdate(this as unknown as ClassComponentInstance, callback);
114
+ };
115
+
116
+ Component.prototype.render = function render(): ReactCompatNode {
117
+ return null;
118
+ };
119
+
120
+ export interface PureComponent<
68
121
  P extends Record<string, unknown> = Record<string, unknown>,
69
122
  S extends Record<string, unknown> = Record<string, unknown>,
70
123
  > extends Component<P, S> {
71
- shouldComponentUpdate(nextProps: P, nextState: S): boolean {
72
- return !shallowEqual(this.props, nextProps) || !shallowEqual(this.state ?? {}, nextState ?? {});
73
- }
124
+ shouldComponentUpdate(nextProps: P, nextState: S): boolean;
125
+ }
126
+
127
+ export interface PureComponentConstructor {
128
+ new <
129
+ P extends Record<string, unknown> = Record<string, unknown>,
130
+ S extends Record<string, unknown> = Record<string, unknown>,
131
+ >(props: P): PureComponent<P, S>;
132
+ <
133
+ P extends Record<string, unknown> = Record<string, unknown>,
134
+ S extends Record<string, unknown> = Record<string, unknown>,
135
+ >(this: PureComponent<P, S>, props: P): void;
136
+ prototype: PureComponent<any, any>;
74
137
  }
75
138
 
139
+ export const PureComponent: PureComponentConstructor = function PureComponent<
140
+ P extends Record<string, unknown> = Record<string, unknown>,
141
+ S extends Record<string, unknown> = Record<string, unknown>,
142
+ >(this: PureComponent<P, S>, props: P): void {
143
+ (Component as unknown as (this: unknown, props: unknown) => void).call(
144
+ this,
145
+ props,
146
+ );
147
+ } as PureComponentConstructor;
148
+
149
+ PureComponent.prototype = Object.create(Component.prototype) as PureComponent<any, any>;
150
+ PureComponent.prototype.constructor = PureComponent;
151
+ PureComponent.prototype.shouldComponentUpdate = function shouldComponentUpdate<
152
+ P extends Record<string, unknown>,
153
+ S extends Record<string, unknown>,
154
+ >(
155
+ this: PureComponent<P, S>,
156
+ nextProps: P,
157
+ nextState: S,
158
+ ): boolean {
159
+ return !shallowEqual(this.props, nextProps) || !shallowEqual(this.state ?? {}, nextState ?? {});
160
+ };
161
+
76
162
  interface ClassLifecycleSnapshot {
77
163
  previousState?: Record<string, unknown>;
164
+ nextState?: Record<string, unknown>;
78
165
  force?: boolean;
79
166
  snapshot?: unknown;
80
167
  }
81
168
 
82
- const classLifecycleSnapshots = new WeakMap<
83
- ClassComponentInstance,
84
- ClassLifecycleSnapshot
85
- >();
169
+ interface ClassUpdateContext {
170
+ runtime: RootRuntime;
171
+ path: string;
172
+ }
173
+
174
+ interface ClassComponentGlobalState {
175
+ lifecycleSnapshots: WeakMap<ClassComponentInstance, ClassLifecycleSnapshot>;
176
+ updateContexts: WeakMap<ClassComponentInstance, ClassUpdateContext>;
177
+ }
178
+
179
+ const CLASS_COMPONENT_STATE_KEY = Symbol.for("modular.react.class_component_state");
180
+ const classComponentGlobalState =
181
+ ((globalThis as typeof globalThis & Record<symbol, ClassComponentGlobalState | undefined>)[
182
+ CLASS_COMPONENT_STATE_KEY
183
+ ] ??= {
184
+ lifecycleSnapshots: new WeakMap(),
185
+ updateContexts: new WeakMap(),
186
+ });
187
+ const classLifecycleSnapshots = classComponentGlobalState.lifecycleSnapshots;
188
+ const classUpdateContexts = classComponentGlobalState.updateContexts;
86
189
 
87
190
  export type ClassComponentRenderResult =
88
191
  | {
@@ -152,6 +255,7 @@ export function renderClassComponentWithRuntime(
152
255
  props: Record<string, unknown>,
153
256
  runtime: RootRuntime,
154
257
  path: string,
258
+ options: { hasDirtyDescendant?: boolean } = {},
155
259
  ): ClassComponentRenderResult {
156
260
  return renderWithRootRuntime(runtime, path, () => {
157
261
  const instanceRef = useRef<ClassComponentInstance | undefined>(undefined);
@@ -163,6 +267,7 @@ export function renderClassComponentWithRuntime(
163
267
 
164
268
  if (hasDifferentType) {
165
269
  classLifecycleSnapshots.delete(previousInstance);
270
+ classUpdateContexts.delete(previousInstance);
166
271
  didCommitRef.current = false;
167
272
  instanceRef.current = undefined;
168
273
  }
@@ -176,15 +281,20 @@ export function renderClassComponentWithRuntime(
176
281
  const previousState = snapshot?.previousState ?? instance.state ?? {};
177
282
 
178
283
  instanceRef.current = instance;
179
- installClassUpdateMethods(instance, runtime);
180
- applyDerivedStateFromProps(type, instance, props, previousState);
181
- const nextState = instance.state ?? {};
284
+ installClassUpdateMethods(instance, runtime, path);
285
+ const nextState = resolveDerivedStateFromProps(
286
+ type,
287
+ props,
288
+ snapshot?.nextState ?? instance.state ?? {},
289
+ );
182
290
  const shouldSkipUpdate =
183
291
  didCommitRef.current &&
184
292
  snapshot?.force !== true &&
293
+ options.hasDirtyDescendant !== true &&
185
294
  instance.shouldComponentUpdate?.(props, nextState) === false;
186
295
 
187
296
  instance.props = props;
297
+ instance.state = nextState;
188
298
  installClassLifecycleEffects(
189
299
  instance,
190
300
  didCommitRef,
@@ -221,7 +331,7 @@ export function renderClassComponentWithRuntime(
221
331
 
222
332
  return { kind: "render", node: fallbackNode, instance, type };
223
333
  }
224
- });
334
+ }, CLASS_COMPONENT_RUNTIME_OWNER);
225
335
  }
226
336
 
227
337
  export function applyDerivedStateFromProps(
@@ -230,14 +340,22 @@ export function applyDerivedStateFromProps(
230
340
  nextProps: Record<string, unknown>,
231
341
  previousState: Record<string, unknown>,
232
342
  ): void {
343
+ instance.state = resolveDerivedStateFromProps(type, nextProps, previousState);
344
+ }
345
+
346
+ export function resolveDerivedStateFromProps(
347
+ type: ClassComponentType,
348
+ nextProps: Record<string, unknown>,
349
+ previousState: Record<string, unknown>,
350
+ ): Record<string, unknown> {
233
351
  const derivedState = type.getDerivedStateFromProps?.(nextProps, previousState);
234
352
 
235
353
  if (derivedState === undefined || derivedState === null) {
236
- return;
354
+ return previousState;
237
355
  }
238
356
 
239
- instance.state = {
240
- ...instance.state,
357
+ return {
358
+ ...previousState,
241
359
  ...derivedState,
242
360
  };
243
361
  }
@@ -329,35 +447,78 @@ export function reconcileErrorBoundary(
329
447
  function installClassUpdateMethods(
330
448
  instance: ClassComponentInstance,
331
449
  runtime: RootRuntime,
450
+ path: string,
332
451
  ): void {
333
- instance.setState = (partial, callback): void => {
334
- const previousState = instance.state ?? {};
335
- if (!classLifecycleSnapshots.has(instance)) {
336
- classLifecycleSnapshots.set(instance, { previousState });
337
- }
338
- const nextPartial =
339
- typeof partial === "function"
340
- ? partial(previousState, instance.props)
341
- : partial;
342
-
343
- if (nextPartial !== null) {
344
- instance.state = {
345
- ...previousState,
346
- ...nextPartial,
347
- };
348
- }
452
+ classUpdateContexts.set(instance, { runtime, path });
453
+ instance.setState = Component.prototype.setState;
454
+ instance.forceUpdate = Component.prototype.forceUpdate;
455
+ }
456
+
457
+ function enqueueClassSetState(
458
+ instance: ClassComponentInstance,
459
+ partial: Parameters<NonNullable<ClassComponentInstance["setState"]>>[0],
460
+ callback?: () => void,
461
+ ): void {
462
+ const updateContext = classUpdateContexts.get(instance);
349
463
 
350
- runtime.rerender();
464
+ if (updateContext === undefined) {
351
465
  callback?.call(instance);
352
- };
353
- instance.forceUpdate = (callback): void => {
354
- classLifecycleSnapshots.set(instance, {
355
- previousState: instance.state ?? {},
356
- force: true,
357
- });
358
- runtime.rerender();
466
+ return;
467
+ }
468
+
469
+ const snapshot = classLifecycleSnapshots.get(instance);
470
+ const previousState = snapshot?.previousState ?? instance.state ?? {};
471
+ const baseState = snapshot?.nextState ?? instance.state ?? {};
472
+ const nextPartial =
473
+ typeof partial === "function"
474
+ ? partial(baseState, instance.props)
475
+ : partial;
476
+
477
+ const nextState =
478
+ nextPartial === null
479
+ ? baseState
480
+ : {
481
+ ...baseState,
482
+ ...nextPartial,
483
+ };
484
+
485
+ classLifecycleSnapshots.set(instance, {
486
+ ...snapshot,
487
+ previousState,
488
+ nextState,
489
+ });
490
+
491
+ markClassInstanceDirty(updateContext);
492
+ callback?.call(instance);
493
+ }
494
+
495
+ function enqueueClassForceUpdate(
496
+ instance: ClassComponentInstance,
497
+ callback?: () => void,
498
+ ): void {
499
+ const updateContext = classUpdateContexts.get(instance);
500
+
501
+ if (updateContext === undefined) {
359
502
  callback?.call(instance);
360
- };
503
+ return;
504
+ }
505
+
506
+ classLifecycleSnapshots.set(instance, {
507
+ previousState: instance.state ?? {},
508
+ force: true,
509
+ });
510
+ markClassInstanceDirty(updateContext);
511
+ callback?.call(instance);
512
+ }
513
+
514
+ function markClassInstanceDirty(updateContext: ClassUpdateContext): void {
515
+ const runtimeInstance = updateContext.runtime.instances.get(updateContext.path) as
516
+ | { dirty?: boolean }
517
+ | undefined;
518
+ if (runtimeInstance !== undefined) {
519
+ runtimeInstance.dirty = true;
520
+ }
521
+ updateContext.runtime.rerender();
361
522
  }
362
523
 
363
524
  function installClassLifecycleEffects(
@@ -369,7 +530,10 @@ function installClassLifecycleEffects(
369
530
  replacedInstance?: ClassComponentInstance,
370
531
  ): void {
371
532
  useLayoutEffect(() => {
372
- replacedInstance?.componentWillUnmount?.();
533
+ if (replacedInstance !== undefined) {
534
+ replacedInstance.componentWillUnmount?.();
535
+ classUpdateContexts.delete(replacedInstance);
536
+ }
373
537
 
374
538
  if (skipUpdate) {
375
539
  classLifecycleSnapshots.delete(instance);
@@ -394,6 +558,7 @@ function installClassLifecycleEffects(
394
558
  return () => {
395
559
  instance.componentWillUnmount?.();
396
560
  classLifecycleSnapshots.delete(instance);
561
+ classUpdateContexts.delete(instance);
397
562
  };
398
563
  }, []);
399
564
  }
package/src/context.ts CHANGED
@@ -1,5 +1,5 @@
1
- const REACT_COMPAT_PROVIDER_TYPE = Symbol.for("modular.react.provider");
2
- const REACT_COMPAT_CONSUMER_TYPE = Symbol.for("modular.react.consumer");
1
+ const REACT_COMPAT_PROVIDER_TYPE = Symbol.for("react.context");
2
+ const REACT_COMPAT_CONSUMER_TYPE = Symbol.for("react.consumer");
3
3
 
4
4
  export interface ReactCompatContext<T> {
5
5
  defaultValue: T;
@@ -9,18 +9,50 @@ export interface ReactCompatContext<T> {
9
9
  displayName: string | undefined;
10
10
  }
11
11
 
12
+ export interface ReactCompatExternalContext<T> {
13
+ $$typeof: typeof REACT_COMPAT_PROVIDER_TYPE;
14
+ _currentValue?: T;
15
+ _currentValue2?: T;
16
+ _defaultValue?: T;
17
+ Provider?: unknown;
18
+ Consumer?: unknown;
19
+ displayName?: string | undefined;
20
+ }
21
+
22
+ export type ReactCompatContextLike<T> =
23
+ | ReactCompatContext<T>
24
+ | ReactCompatExternalContext<T>;
25
+
12
26
  export interface ReactCompatProvider<T> {
13
27
  $$typeof: typeof REACT_COMPAT_PROVIDER_TYPE;
14
- context: ReactCompatContext<T>;
28
+ context?: ReactCompatContextLike<T>;
15
29
  displayName: string | undefined;
16
30
  }
17
31
 
18
32
  export interface ReactCompatConsumer<T> {
19
33
  $$typeof: typeof REACT_COMPAT_CONSUMER_TYPE;
20
- context: ReactCompatContext<T>;
34
+ context?: ReactCompatContextLike<T>;
35
+ _context?: ReactCompatContextLike<T>;
21
36
  displayName: string | undefined;
22
37
  }
23
38
 
39
+ type ContextReadObserver = (context: ReactCompatContextLike<unknown>, value: unknown) => void;
40
+ interface ContextReadObserverState {
41
+ current: ContextReadObserver | undefined;
42
+ }
43
+
44
+ const externalContextValues = new WeakMap<object, unknown[]>();
45
+
46
+ const CONTEXT_READ_OBSERVER_STATE_KEY = Symbol.for(
47
+ "modular.react.context_read_observer_state",
48
+ );
49
+ const contextReadObserverState =
50
+ ((globalThis as typeof globalThis & Record<symbol, ContextReadObserverState | undefined>)[
51
+ CONTEXT_READ_OBSERVER_STATE_KEY
52
+ ] ??= {
53
+ current: undefined,
54
+ });
55
+
24
56
  export function createContext<T>(defaultValue: T): ReactCompatContext<T> {
25
57
  const context: ReactCompatContext<T> = {
26
58
  defaultValue,
@@ -62,8 +94,37 @@ function installContextDisplayName<T>(context: ReactCompatContext<T>): void {
62
94
  });
63
95
  }
64
96
 
65
- export function useContext<T>(context: ReactCompatContext<T>): T {
66
- return context.values.at(-1) ?? context.defaultValue;
97
+ export function useContext<T>(context: ReactCompatContextLike<T>): T {
98
+ const value = readContextValue(context);
99
+ contextReadObserverState.current?.(context as ReactCompatContextLike<unknown>, value);
100
+ return value;
101
+ }
102
+
103
+ export function readContextValue<T>(context: ReactCompatContextLike<T>): T {
104
+ if (isInternalContextRecord(context)) {
105
+ return context.values.at(-1) ?? context.defaultValue;
106
+ }
107
+
108
+ return (
109
+ (externalContextValues.get(context)?.at(-1) as T | undefined) ??
110
+ context._currentValue ??
111
+ context._currentValue2 ??
112
+ (context._defaultValue as T)
113
+ );
114
+ }
115
+
116
+ export function withContextReadObserver<T>(
117
+ observer: ContextReadObserver,
118
+ render: () => T,
119
+ ): T {
120
+ const previousObserver = contextReadObserverState.current;
121
+ contextReadObserverState.current = observer;
122
+
123
+ try {
124
+ return render();
125
+ } finally {
126
+ contextReadObserverState.current = previousObserver;
127
+ }
67
128
  }
68
129
 
69
130
  export function isReactCompatProvider(
@@ -117,11 +178,27 @@ export function pushContextProvider<T>(
117
178
  provider: ReactCompatProvider<T>,
118
179
  value: T,
119
180
  ): void {
120
- provider.context.values.push(value);
181
+ const context = providerContext(provider);
182
+
183
+ if (isInternalContextRecord(context)) {
184
+ context.values.push(value);
185
+ return;
186
+ }
187
+
188
+ const values = externalContextValues.get(context) ?? [];
189
+ values.push(value);
190
+ externalContextValues.set(context, values);
121
191
  }
122
192
 
123
193
  export function popContextProvider<T>(provider: ReactCompatProvider<T>): void {
124
- provider.context.values.pop();
194
+ const context = providerContext(provider);
195
+
196
+ if (isInternalContextRecord(context)) {
197
+ context.values.pop();
198
+ return;
199
+ }
200
+
201
+ externalContextValues.get(context)?.pop();
125
202
  }
126
203
 
127
204
  export function renderContextProviderToString<T>(
@@ -136,5 +213,27 @@ export function renderContextConsumerToString<T>(
136
213
  consumer: ReactCompatConsumer<T>,
137
214
  render: (value: T) => string,
138
215
  ): string {
139
- return render(useContext(consumer.context));
216
+ return render(useContext(consumerContext(consumer)));
217
+ }
218
+
219
+ export function providerContext<T>(
220
+ provider: ReactCompatProvider<T>,
221
+ ): ReactCompatContextLike<T> {
222
+ return provider.context ?? (provider as unknown as ReactCompatExternalContext<T>);
223
+ }
224
+
225
+ export function consumerContext<T>(
226
+ consumer: ReactCompatConsumer<T>,
227
+ ): ReactCompatContextLike<T> {
228
+ return (
229
+ consumer.context ??
230
+ consumer._context ??
231
+ (consumer as unknown as ReactCompatExternalContext<T>)
232
+ );
233
+ }
234
+
235
+ function isInternalContextRecord<T>(
236
+ context: ReactCompatContextLike<T>,
237
+ ): context is ReactCompatContext<T> {
238
+ return "values" in context && Array.isArray(context.values);
140
239
  }