@reckona/mreact-compat 0.0.92 → 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/dist/class-component.d.ts +2 -0
- package/dist/class-component.d.ts.map +1 -1
- package/dist/class-component.js +66 -3
- package/dist/class-component.js.map +1 -1
- package/dist/host-reconciler.d.ts.map +1 -1
- package/dist/host-reconciler.js +33 -6
- package/dist/host-reconciler.js.map +1 -1
- package/package.json +2 -2
- package/src/class-component.ts +101 -4
- package/src/host-reconciler.ts +51 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reckona/mreact-compat",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.93",
|
|
4
4
|
"description": "React-compatible runtime implementation for mreact.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"compatibility",
|
|
@@ -69,6 +69,6 @@
|
|
|
69
69
|
"access": "public"
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
|
-
"@reckona/mreact-shared": "0.0.
|
|
72
|
+
"@reckona/mreact-shared": "0.0.93"
|
|
73
73
|
}
|
|
74
74
|
}
|
package/src/class-component.ts
CHANGED
|
@@ -174,6 +174,8 @@ interface ClassUpdateContext {
|
|
|
174
174
|
interface ClassComponentGlobalState {
|
|
175
175
|
lifecycleSnapshots: WeakMap<ClassComponentInstance, ClassLifecycleSnapshot>;
|
|
176
176
|
updateContexts: WeakMap<ClassComponentInstance, ClassUpdateContext>;
|
|
177
|
+
pendingInstancesByRuntime: WeakMap<RootRuntime, Map<string, ClassComponentInstance>>;
|
|
178
|
+
dirtyPathsByRuntime: WeakMap<RootRuntime, Set<string>>;
|
|
177
179
|
}
|
|
178
180
|
|
|
179
181
|
const CLASS_COMPONENT_STATE_KEY = Symbol.for("modular.react.class_component_state");
|
|
@@ -183,9 +185,13 @@ const classComponentGlobalState =
|
|
|
183
185
|
] ??= {
|
|
184
186
|
lifecycleSnapshots: new WeakMap(),
|
|
185
187
|
updateContexts: new WeakMap(),
|
|
188
|
+
pendingInstancesByRuntime: new WeakMap(),
|
|
189
|
+
dirtyPathsByRuntime: new WeakMap(),
|
|
186
190
|
});
|
|
187
191
|
const classLifecycleSnapshots = classComponentGlobalState.lifecycleSnapshots;
|
|
188
192
|
const classUpdateContexts = classComponentGlobalState.updateContexts;
|
|
193
|
+
const classPendingInstancesByRuntime = classComponentGlobalState.pendingInstancesByRuntime;
|
|
194
|
+
const classDirtyPathsByRuntime = classComponentGlobalState.dirtyPathsByRuntime;
|
|
189
195
|
|
|
190
196
|
export type ClassComponentRenderResult =
|
|
191
197
|
| {
|
|
@@ -255,11 +261,27 @@ export function renderClassComponentWithRuntime(
|
|
|
255
261
|
props: Record<string, unknown>,
|
|
256
262
|
runtime: RootRuntime,
|
|
257
263
|
path: string,
|
|
258
|
-
options: {
|
|
264
|
+
options: {
|
|
265
|
+
currentInstance?: ClassComponentInstance;
|
|
266
|
+
hasDirtyDescendant?: boolean;
|
|
267
|
+
} = {},
|
|
259
268
|
): ClassComponentRenderResult {
|
|
260
269
|
return renderWithRootRuntime(runtime, path, () => {
|
|
261
270
|
const instanceRef = useRef<ClassComponentInstance | undefined>(undefined);
|
|
262
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
|
+
|
|
263
285
|
const previousInstance = instanceRef.current;
|
|
264
286
|
const hasDifferentType =
|
|
265
287
|
previousInstance !== undefined && !(previousInstance instanceof type);
|
|
@@ -282,6 +304,7 @@ export function renderClassComponentWithRuntime(
|
|
|
282
304
|
|
|
283
305
|
instanceRef.current = instance;
|
|
284
306
|
installClassUpdateMethods(instance, runtime, path);
|
|
307
|
+
clearPendingClassUpdate(runtime, path);
|
|
285
308
|
const nextState = resolveDerivedStateFromProps(
|
|
286
309
|
type,
|
|
287
310
|
props,
|
|
@@ -488,7 +511,7 @@ function enqueueClassSetState(
|
|
|
488
511
|
nextState,
|
|
489
512
|
});
|
|
490
513
|
|
|
491
|
-
markClassInstanceDirty(updateContext);
|
|
514
|
+
markClassInstanceDirty(instance, updateContext);
|
|
492
515
|
callback?.call(instance);
|
|
493
516
|
}
|
|
494
517
|
|
|
@@ -507,11 +530,15 @@ function enqueueClassForceUpdate(
|
|
|
507
530
|
previousState: instance.state ?? {},
|
|
508
531
|
force: true,
|
|
509
532
|
});
|
|
510
|
-
markClassInstanceDirty(updateContext);
|
|
533
|
+
markClassInstanceDirty(instance, updateContext);
|
|
511
534
|
callback?.call(instance);
|
|
512
535
|
}
|
|
513
536
|
|
|
514
|
-
function markClassInstanceDirty(
|
|
537
|
+
function markClassInstanceDirty(
|
|
538
|
+
instance: ClassComponentInstance,
|
|
539
|
+
updateContext: ClassUpdateContext,
|
|
540
|
+
): void {
|
|
541
|
+
markPendingClassUpdate(instance, updateContext);
|
|
515
542
|
const runtimeInstance = updateContext.runtime.instances.get(updateContext.path) as
|
|
516
543
|
| { dirty?: boolean }
|
|
517
544
|
| undefined;
|
|
@@ -521,6 +548,76 @@ function markClassInstanceDirty(updateContext: ClassUpdateContext): void {
|
|
|
521
548
|
updateContext.runtime.rerender();
|
|
522
549
|
}
|
|
523
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);
|
|
619
|
+
}
|
|
620
|
+
|
|
524
621
|
function installClassLifecycleEffects(
|
|
525
622
|
instance: ClassComponentInstance,
|
|
526
623
|
didCommitRef: { current: boolean },
|
package/src/host-reconciler.ts
CHANGED
|
@@ -51,9 +51,11 @@ import {
|
|
|
51
51
|
} from "./hooks.js";
|
|
52
52
|
import { isThenable } from "./thenable.js";
|
|
53
53
|
import {
|
|
54
|
+
hasDirtyClassUpdate,
|
|
54
55
|
isClassComponentType,
|
|
55
56
|
recoverClassComponentError,
|
|
56
57
|
renderClassComponentWithRuntime,
|
|
58
|
+
type ClassComponentInstance,
|
|
57
59
|
} from "./class-component.js";
|
|
58
60
|
import { areMemoPropsEqual, getPendingProps, shallowEqual } from "./prop-comparison.js";
|
|
59
61
|
import {
|
|
@@ -1204,7 +1206,7 @@ function createHostFiberImpl(
|
|
|
1204
1206
|
|
|
1205
1207
|
if (
|
|
1206
1208
|
previousMemoState !== undefined &&
|
|
1207
|
-
!hasDirtyInstance(runtime, previousMemoState.instanceKeys) &&
|
|
1209
|
+
!hasDirtyInstance(runtime, previousMemoState.instanceKeys, memoPath) &&
|
|
1208
1210
|
!hasUnflushedMountEffectInstance(runtime, previousMemoState.instanceKeys) &&
|
|
1209
1211
|
areMemoPropsEqual(memoType, previousMemoState.props, node.props)
|
|
1210
1212
|
) {
|
|
@@ -1315,12 +1317,25 @@ function createHostFiberImpl(
|
|
|
1315
1317
|
: createFiber("class-component", node.props, key);
|
|
1316
1318
|
fiber.type = classType;
|
|
1317
1319
|
const previousClassChildKeys = collectInstanceKeys(runtime, `${path}.class`);
|
|
1320
|
+
const currentClassInstance =
|
|
1321
|
+
current?.tag === "class-component" && current.type === classType
|
|
1322
|
+
? (current.stateNode as ClassComponentInstance)
|
|
1323
|
+
: undefined;
|
|
1318
1324
|
const rendered = renderClassComponentWithRuntime(
|
|
1319
1325
|
classType,
|
|
1320
1326
|
node.props,
|
|
1321
1327
|
runtime,
|
|
1322
1328
|
path,
|
|
1323
|
-
{
|
|
1329
|
+
{
|
|
1330
|
+
...(currentClassInstance === undefined
|
|
1331
|
+
? {}
|
|
1332
|
+
: { currentInstance: currentClassInstance }),
|
|
1333
|
+
hasDirtyDescendant: hasDirtyInstance(
|
|
1334
|
+
runtime,
|
|
1335
|
+
previousClassChildKeys,
|
|
1336
|
+
`${path}.class`,
|
|
1337
|
+
),
|
|
1338
|
+
},
|
|
1324
1339
|
);
|
|
1325
1340
|
applyRef(node.ref, rendered.kind === "skip" ? current?.stateNode : rendered.instance);
|
|
1326
1341
|
|
|
@@ -1398,7 +1413,7 @@ function createHostFiberImpl(
|
|
|
1398
1413
|
runtime.strictReplayDepth === 0 &&
|
|
1399
1414
|
previousFunctionState !== undefined &&
|
|
1400
1415
|
(canReuseSameElement || canReuseExternalStoreSnapshot) &&
|
|
1401
|
-
!hasDirtyInstance(runtime, previousFunctionState.instanceKeys) &&
|
|
1416
|
+
!hasDirtyInstance(runtime, previousFunctionState.instanceKeys, path) &&
|
|
1402
1417
|
!hasUnflushedMountEffectInstance(runtime, previousFunctionState.instanceKeys) &&
|
|
1403
1418
|
!hasPendingAsyncChild(current?.child)
|
|
1404
1419
|
) {
|
|
@@ -1495,6 +1510,7 @@ function createHostFiberImpl(
|
|
|
1495
1510
|
current?.tag === "host-component" &&
|
|
1496
1511
|
current.type === node.type &&
|
|
1497
1512
|
Object.is(hostFiberChildrenProp(current.memoizedProps), node.props.children) &&
|
|
1513
|
+
!hasDirtyInstance(runtime, [], `${path}.c`) &&
|
|
1498
1514
|
canReuseStaticHostSubtree(current.child)
|
|
1499
1515
|
) {
|
|
1500
1516
|
fiber.child = current.child;
|
|
@@ -3064,11 +3080,40 @@ function markActiveInstanceKeys(runtime: RootRuntime, keys: readonly string[]):
|
|
|
3064
3080
|
}
|
|
3065
3081
|
}
|
|
3066
3082
|
|
|
3067
|
-
function hasDirtyInstance(
|
|
3068
|
-
|
|
3083
|
+
function hasDirtyInstance(
|
|
3084
|
+
runtime: RootRuntime | undefined,
|
|
3085
|
+
keys: readonly string[],
|
|
3086
|
+
prefix?: string,
|
|
3087
|
+
): boolean {
|
|
3088
|
+
if (runtime === undefined) {
|
|
3089
|
+
return false;
|
|
3090
|
+
}
|
|
3091
|
+
|
|
3092
|
+
if (hasDirtyClassUpdate(runtime, keys, prefix)) {
|
|
3093
|
+
return true;
|
|
3094
|
+
}
|
|
3095
|
+
|
|
3096
|
+
if (keys.some(
|
|
3069
3097
|
(key) =>
|
|
3070
3098
|
(runtime.instances.get(key) as { dirty?: boolean } | undefined)?.dirty === true,
|
|
3071
|
-
)
|
|
3099
|
+
)) {
|
|
3100
|
+
return true;
|
|
3101
|
+
}
|
|
3102
|
+
|
|
3103
|
+
if (prefix === undefined) {
|
|
3104
|
+
return false;
|
|
3105
|
+
}
|
|
3106
|
+
|
|
3107
|
+
for (const [key, instance] of runtime.instances) {
|
|
3108
|
+
if (
|
|
3109
|
+
(key === prefix || key.startsWith(`${prefix}.`)) &&
|
|
3110
|
+
(instance as { dirty?: boolean }).dirty === true
|
|
3111
|
+
) {
|
|
3112
|
+
return true;
|
|
3113
|
+
}
|
|
3114
|
+
}
|
|
3115
|
+
|
|
3116
|
+
return false;
|
|
3072
3117
|
}
|
|
3073
3118
|
|
|
3074
3119
|
function hasUnflushedMountEffectInstance(runtime: RootRuntime, keys: readonly string[]): boolean {
|