@reckona/mreact-compat 0.0.91 → 0.0.93
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.
- package/README.md +1 -0
- package/dist/class-component.d.ts +22 -6
- package/dist/class-component.d.ts.map +1 -1
- package/dist/class-component.js +157 -51
- package/dist/class-component.js.map +1 -1
- package/dist/context.d.ts +19 -3
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +55 -6
- package/dist/context.js.map +1 -1
- package/dist/dom-children.d.ts +2 -0
- package/dist/dom-children.d.ts.map +1 -1
- package/dist/dom-children.js +103 -1
- package/dist/dom-children.js.map +1 -1
- package/dist/dom-host-rules.d.ts +10 -0
- package/dist/dom-host-rules.d.ts.map +1 -0
- package/dist/dom-host-rules.js +86 -0
- package/dist/dom-host-rules.js.map +1 -0
- package/dist/dom-props.d.ts +3 -2
- package/dist/dom-props.d.ts.map +1 -1
- package/dist/dom-props.js +229 -33
- package/dist/dom-props.js.map +1 -1
- package/dist/element.d.ts +9 -4
- package/dist/element.d.ts.map +1 -1
- package/dist/element.js +101 -26
- package/dist/element.js.map +1 -1
- package/dist/event-listeners.d.ts +4 -4
- package/dist/event-listeners.d.ts.map +1 -1
- package/dist/event-listeners.js +1 -1
- package/dist/event-listeners.js.map +1 -1
- package/dist/event-types.d.ts +10 -0
- package/dist/event-types.d.ts.map +1 -1
- package/dist/event-types.js.map +1 -1
- package/dist/events.js +22 -1
- package/dist/events.js.map +1 -1
- package/dist/fiber-commit.d.ts +2 -1
- package/dist/fiber-commit.d.ts.map +1 -1
- package/dist/fiber-commit.js +13 -1
- package/dist/fiber-commit.js.map +1 -1
- package/dist/fiber-reconciler.d.ts.map +1 -1
- package/dist/fiber-reconciler.js +28 -7
- package/dist/fiber-reconciler.js.map +1 -1
- package/dist/fiber-work-loop.d.ts.map +1 -1
- package/dist/fiber-work-loop.js +4 -3
- package/dist/fiber-work-loop.js.map +1 -1
- package/dist/fiber.d.ts +5 -0
- package/dist/fiber.d.ts.map +1 -1
- package/dist/fiber.js +9 -0
- package/dist/fiber.js.map +1 -1
- package/dist/hooks-entry.d.ts +3 -0
- package/dist/hooks-entry.d.ts.map +1 -0
- package/dist/hooks-entry.js +2 -0
- package/dist/hooks-entry.js.map +1 -0
- package/dist/hooks.d.ts +39 -5
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +373 -326
- package/dist/hooks.js.map +1 -1
- package/dist/host-reconciler.d.ts +3 -0
- package/dist/host-reconciler.d.ts.map +1 -1
- package/dist/host-reconciler.js +1183 -68
- package/dist/host-reconciler.js.map +1 -1
- package/dist/hydration.d.ts +1 -1
- package/dist/hydration.d.ts.map +1 -1
- package/dist/hydration.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/react-default.d.ts +4 -4
- package/dist/react-default.d.ts.map +1 -1
- package/dist/react-default.js +2 -1
- package/dist/react-default.js.map +1 -1
- package/dist/reconciler.d.ts.map +1 -1
- package/dist/reconciler.js +38 -22
- package/dist/reconciler.js.map +1 -1
- package/dist/root.d.ts.map +1 -1
- package/dist/root.js +48 -13
- package/dist/root.js.map +1 -1
- package/dist/server-render.d.ts +6 -0
- package/dist/server-render.d.ts.map +1 -0
- package/dist/server-render.js +307 -0
- package/dist/server-render.js.map +1 -0
- package/package.json +6 -2
- package/src/class-component.ts +313 -51
- package/src/context.ts +108 -9
- package/src/dom-children.ts +155 -1
- package/src/dom-host-rules.ts +115 -0
- package/src/dom-props.ts +297 -46
- package/src/element.ts +141 -31
- package/src/event-listeners.ts +6 -6
- package/src/event-types.ts +10 -0
- package/src/events.ts +32 -10
- package/src/fiber-commit.ts +16 -1
- package/src/fiber-reconciler.ts +39 -6
- package/src/fiber-work-loop.ts +4 -3
- package/src/fiber.ts +14 -0
- package/src/hooks-entry.ts +24 -0
- package/src/hooks.ts +482 -479
- package/src/host-reconciler.ts +1662 -83
- package/src/hydration.ts +1 -1
- package/src/index.ts +1 -1
- package/src/react-default.ts +1 -1
- package/src/reconciler.ts +61 -22
- package/src/root.ts +55 -12
- package/src/server-render.ts +478 -0
package/src/class-component.ts
CHANGED
|
@@ -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,150 @@ export interface ClassComponentType {
|
|
|
46
48
|
getDerivedStateFromError?: (error: Error) => Record<string, unknown> | null;
|
|
47
49
|
}
|
|
48
50
|
|
|
49
|
-
export
|
|
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
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
63
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
+
pendingInstancesByRuntime: WeakMap<RootRuntime, Map<string, ClassComponentInstance>>;
|
|
178
|
+
dirtyPathsByRuntime: WeakMap<RootRuntime, Set<string>>;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const CLASS_COMPONENT_STATE_KEY = Symbol.for("modular.react.class_component_state");
|
|
182
|
+
const classComponentGlobalState =
|
|
183
|
+
((globalThis as typeof globalThis & Record<symbol, ClassComponentGlobalState | undefined>)[
|
|
184
|
+
CLASS_COMPONENT_STATE_KEY
|
|
185
|
+
] ??= {
|
|
186
|
+
lifecycleSnapshots: new WeakMap(),
|
|
187
|
+
updateContexts: new WeakMap(),
|
|
188
|
+
pendingInstancesByRuntime: new WeakMap(),
|
|
189
|
+
dirtyPathsByRuntime: new WeakMap(),
|
|
190
|
+
});
|
|
191
|
+
const classLifecycleSnapshots = classComponentGlobalState.lifecycleSnapshots;
|
|
192
|
+
const classUpdateContexts = classComponentGlobalState.updateContexts;
|
|
193
|
+
const classPendingInstancesByRuntime = classComponentGlobalState.pendingInstancesByRuntime;
|
|
194
|
+
const classDirtyPathsByRuntime = classComponentGlobalState.dirtyPathsByRuntime;
|
|
86
195
|
|
|
87
196
|
export type ClassComponentRenderResult =
|
|
88
197
|
| {
|
|
@@ -152,10 +261,27 @@ export function renderClassComponentWithRuntime(
|
|
|
152
261
|
props: Record<string, unknown>,
|
|
153
262
|
runtime: RootRuntime,
|
|
154
263
|
path: string,
|
|
264
|
+
options: {
|
|
265
|
+
currentInstance?: ClassComponentInstance;
|
|
266
|
+
hasDirtyDescendant?: boolean;
|
|
267
|
+
} = {},
|
|
155
268
|
): ClassComponentRenderResult {
|
|
156
269
|
return renderWithRootRuntime(runtime, path, () => {
|
|
157
270
|
const instanceRef = useRef<ClassComponentInstance | undefined>(undefined);
|
|
158
271
|
const didCommitRef = useRef(false);
|
|
272
|
+
const currentInstance =
|
|
273
|
+
options.currentInstance instanceof type
|
|
274
|
+
? options.currentInstance
|
|
275
|
+
: getPendingClassInstance(runtime, path, type);
|
|
276
|
+
|
|
277
|
+
if (
|
|
278
|
+
currentInstance !== undefined &&
|
|
279
|
+
instanceRef.current !== currentInstance
|
|
280
|
+
) {
|
|
281
|
+
instanceRef.current = currentInstance;
|
|
282
|
+
didCommitRef.current = true;
|
|
283
|
+
}
|
|
284
|
+
|
|
159
285
|
const previousInstance = instanceRef.current;
|
|
160
286
|
const hasDifferentType =
|
|
161
287
|
previousInstance !== undefined && !(previousInstance instanceof type);
|
|
@@ -163,6 +289,7 @@ export function renderClassComponentWithRuntime(
|
|
|
163
289
|
|
|
164
290
|
if (hasDifferentType) {
|
|
165
291
|
classLifecycleSnapshots.delete(previousInstance);
|
|
292
|
+
classUpdateContexts.delete(previousInstance);
|
|
166
293
|
didCommitRef.current = false;
|
|
167
294
|
instanceRef.current = undefined;
|
|
168
295
|
}
|
|
@@ -176,15 +303,21 @@ export function renderClassComponentWithRuntime(
|
|
|
176
303
|
const previousState = snapshot?.previousState ?? instance.state ?? {};
|
|
177
304
|
|
|
178
305
|
instanceRef.current = instance;
|
|
179
|
-
installClassUpdateMethods(instance, runtime);
|
|
180
|
-
|
|
181
|
-
const nextState =
|
|
306
|
+
installClassUpdateMethods(instance, runtime, path);
|
|
307
|
+
clearPendingClassUpdate(runtime, path);
|
|
308
|
+
const nextState = resolveDerivedStateFromProps(
|
|
309
|
+
type,
|
|
310
|
+
props,
|
|
311
|
+
snapshot?.nextState ?? instance.state ?? {},
|
|
312
|
+
);
|
|
182
313
|
const shouldSkipUpdate =
|
|
183
314
|
didCommitRef.current &&
|
|
184
315
|
snapshot?.force !== true &&
|
|
316
|
+
options.hasDirtyDescendant !== true &&
|
|
185
317
|
instance.shouldComponentUpdate?.(props, nextState) === false;
|
|
186
318
|
|
|
187
319
|
instance.props = props;
|
|
320
|
+
instance.state = nextState;
|
|
188
321
|
installClassLifecycleEffects(
|
|
189
322
|
instance,
|
|
190
323
|
didCommitRef,
|
|
@@ -221,7 +354,7 @@ export function renderClassComponentWithRuntime(
|
|
|
221
354
|
|
|
222
355
|
return { kind: "render", node: fallbackNode, instance, type };
|
|
223
356
|
}
|
|
224
|
-
});
|
|
357
|
+
}, CLASS_COMPONENT_RUNTIME_OWNER);
|
|
225
358
|
}
|
|
226
359
|
|
|
227
360
|
export function applyDerivedStateFromProps(
|
|
@@ -230,14 +363,22 @@ export function applyDerivedStateFromProps(
|
|
|
230
363
|
nextProps: Record<string, unknown>,
|
|
231
364
|
previousState: Record<string, unknown>,
|
|
232
365
|
): void {
|
|
366
|
+
instance.state = resolveDerivedStateFromProps(type, nextProps, previousState);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
export function resolveDerivedStateFromProps(
|
|
370
|
+
type: ClassComponentType,
|
|
371
|
+
nextProps: Record<string, unknown>,
|
|
372
|
+
previousState: Record<string, unknown>,
|
|
373
|
+
): Record<string, unknown> {
|
|
233
374
|
const derivedState = type.getDerivedStateFromProps?.(nextProps, previousState);
|
|
234
375
|
|
|
235
376
|
if (derivedState === undefined || derivedState === null) {
|
|
236
|
-
return;
|
|
377
|
+
return previousState;
|
|
237
378
|
}
|
|
238
379
|
|
|
239
|
-
|
|
240
|
-
...
|
|
380
|
+
return {
|
|
381
|
+
...previousState,
|
|
241
382
|
...derivedState,
|
|
242
383
|
};
|
|
243
384
|
}
|
|
@@ -329,35 +470,152 @@ export function reconcileErrorBoundary(
|
|
|
329
470
|
function installClassUpdateMethods(
|
|
330
471
|
instance: ClassComponentInstance,
|
|
331
472
|
runtime: RootRuntime,
|
|
473
|
+
path: string,
|
|
332
474
|
): void {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
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
|
-
}
|
|
475
|
+
classUpdateContexts.set(instance, { runtime, path });
|
|
476
|
+
instance.setState = Component.prototype.setState;
|
|
477
|
+
instance.forceUpdate = Component.prototype.forceUpdate;
|
|
478
|
+
}
|
|
349
479
|
|
|
350
|
-
|
|
480
|
+
function enqueueClassSetState(
|
|
481
|
+
instance: ClassComponentInstance,
|
|
482
|
+
partial: Parameters<NonNullable<ClassComponentInstance["setState"]>>[0],
|
|
483
|
+
callback?: () => void,
|
|
484
|
+
): void {
|
|
485
|
+
const updateContext = classUpdateContexts.get(instance);
|
|
486
|
+
|
|
487
|
+
if (updateContext === undefined) {
|
|
351
488
|
callback?.call(instance);
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const snapshot = classLifecycleSnapshots.get(instance);
|
|
493
|
+
const previousState = snapshot?.previousState ?? instance.state ?? {};
|
|
494
|
+
const baseState = snapshot?.nextState ?? instance.state ?? {};
|
|
495
|
+
const nextPartial =
|
|
496
|
+
typeof partial === "function"
|
|
497
|
+
? partial(baseState, instance.props)
|
|
498
|
+
: partial;
|
|
499
|
+
|
|
500
|
+
const nextState =
|
|
501
|
+
nextPartial === null
|
|
502
|
+
? baseState
|
|
503
|
+
: {
|
|
504
|
+
...baseState,
|
|
505
|
+
...nextPartial,
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
classLifecycleSnapshots.set(instance, {
|
|
509
|
+
...snapshot,
|
|
510
|
+
previousState,
|
|
511
|
+
nextState,
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
markClassInstanceDirty(instance, updateContext);
|
|
515
|
+
callback?.call(instance);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function enqueueClassForceUpdate(
|
|
519
|
+
instance: ClassComponentInstance,
|
|
520
|
+
callback?: () => void,
|
|
521
|
+
): void {
|
|
522
|
+
const updateContext = classUpdateContexts.get(instance);
|
|
523
|
+
|
|
524
|
+
if (updateContext === undefined) {
|
|
359
525
|
callback?.call(instance);
|
|
360
|
-
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
classLifecycleSnapshots.set(instance, {
|
|
530
|
+
previousState: instance.state ?? {},
|
|
531
|
+
force: true,
|
|
532
|
+
});
|
|
533
|
+
markClassInstanceDirty(instance, updateContext);
|
|
534
|
+
callback?.call(instance);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
function markClassInstanceDirty(
|
|
538
|
+
instance: ClassComponentInstance,
|
|
539
|
+
updateContext: ClassUpdateContext,
|
|
540
|
+
): void {
|
|
541
|
+
markPendingClassUpdate(instance, updateContext);
|
|
542
|
+
const runtimeInstance = updateContext.runtime.instances.get(updateContext.path) as
|
|
543
|
+
| { dirty?: boolean }
|
|
544
|
+
| undefined;
|
|
545
|
+
if (runtimeInstance !== undefined) {
|
|
546
|
+
runtimeInstance.dirty = true;
|
|
547
|
+
}
|
|
548
|
+
updateContext.runtime.rerender();
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
export function hasDirtyClassUpdate(
|
|
552
|
+
runtime: RootRuntime | undefined,
|
|
553
|
+
keys: readonly string[],
|
|
554
|
+
prefix?: string,
|
|
555
|
+
): boolean {
|
|
556
|
+
if (runtime === undefined) {
|
|
557
|
+
return false;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
const dirtyPaths = classDirtyPathsByRuntime.get(runtime);
|
|
561
|
+
if (dirtyPaths === undefined) {
|
|
562
|
+
return false;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
for (const key of keys) {
|
|
566
|
+
if (dirtyPaths.has(key)) {
|
|
567
|
+
return true;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
if (prefix === undefined) {
|
|
572
|
+
return false;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
for (const dirtyPath of dirtyPaths) {
|
|
576
|
+
if (dirtyPath === prefix || dirtyPath.startsWith(`${prefix}.`)) {
|
|
577
|
+
return true;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
return false;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
function markPendingClassUpdate(
|
|
585
|
+
instance: ClassComponentInstance,
|
|
586
|
+
updateContext: ClassUpdateContext,
|
|
587
|
+
): void {
|
|
588
|
+
let pendingInstances = classPendingInstancesByRuntime.get(updateContext.runtime);
|
|
589
|
+
if (pendingInstances === undefined) {
|
|
590
|
+
pendingInstances = new Map();
|
|
591
|
+
classPendingInstancesByRuntime.set(updateContext.runtime, pendingInstances);
|
|
592
|
+
}
|
|
593
|
+
let dirtyPaths = classDirtyPathsByRuntime.get(updateContext.runtime);
|
|
594
|
+
if (dirtyPaths === undefined) {
|
|
595
|
+
dirtyPaths = new Set();
|
|
596
|
+
classDirtyPathsByRuntime.set(updateContext.runtime, dirtyPaths);
|
|
597
|
+
}
|
|
598
|
+
pendingInstances.set(updateContext.path, instance);
|
|
599
|
+
dirtyPaths.add(updateContext.path);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function getPendingClassInstance(
|
|
603
|
+
runtime: RootRuntime,
|
|
604
|
+
path: string,
|
|
605
|
+
type: ClassComponentType,
|
|
606
|
+
): ClassComponentInstance | undefined {
|
|
607
|
+
const dirtyPaths = classDirtyPathsByRuntime.get(runtime);
|
|
608
|
+
if (dirtyPaths?.has(path) !== true) {
|
|
609
|
+
return undefined;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
const instance = classPendingInstancesByRuntime.get(runtime)?.get(path);
|
|
613
|
+
return instance instanceof type ? instance : undefined;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
function clearPendingClassUpdate(runtime: RootRuntime, path: string): void {
|
|
617
|
+
classDirtyPathsByRuntime.get(runtime)?.delete(path);
|
|
618
|
+
classPendingInstancesByRuntime.get(runtime)?.delete(path);
|
|
361
619
|
}
|
|
362
620
|
|
|
363
621
|
function installClassLifecycleEffects(
|
|
@@ -369,7 +627,10 @@ function installClassLifecycleEffects(
|
|
|
369
627
|
replacedInstance?: ClassComponentInstance,
|
|
370
628
|
): void {
|
|
371
629
|
useLayoutEffect(() => {
|
|
372
|
-
replacedInstance
|
|
630
|
+
if (replacedInstance !== undefined) {
|
|
631
|
+
replacedInstance.componentWillUnmount?.();
|
|
632
|
+
classUpdateContexts.delete(replacedInstance);
|
|
633
|
+
}
|
|
373
634
|
|
|
374
635
|
if (skipUpdate) {
|
|
375
636
|
classLifecycleSnapshots.delete(instance);
|
|
@@ -394,6 +655,7 @@ function installClassLifecycleEffects(
|
|
|
394
655
|
return () => {
|
|
395
656
|
instance.componentWillUnmount?.();
|
|
396
657
|
classLifecycleSnapshots.delete(instance);
|
|
658
|
+
classUpdateContexts.delete(instance);
|
|
397
659
|
};
|
|
398
660
|
}, []);
|
|
399
661
|
}
|
package/src/context.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const REACT_COMPAT_PROVIDER_TYPE = Symbol.for("
|
|
2
|
-
const REACT_COMPAT_CONSUMER_TYPE = Symbol.for("
|
|
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
|
|
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
|
|
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:
|
|
66
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
}
|