[noop] Fix `createContainer` argument order in the Fiber implementation (#35945)
Sebastian "Sebbie" Silbermann committed
Mar 4, 2026 at 14:20 UTC
3bc2d414287e62a7b74731c6c7b837270353a339
12 files changed
+590
-334
.eslintrc.js
+1
@@ -626,6 +626,7 @@ module.exports = {
626
FinalizationRegistry: 'readonly',
627
Exclude: 'readonly',
628
Omit: 'readonly',
629
+ Pick: 'readonly',
630
Keyframe: 'readonly',
631
PropertyIndexedKeyframes: 'readonly',
632
KeyframeAnimationOptions: 'readonly',
packages/react-noop-renderer/src/ReactFiberConfigNoop.js
+15
@@ -7,6 +7,15 @@
7
* @flow
8
*/
9
10
+export * from './ReactFiberConfigNoopHydration';
11
+export * from './ReactFiberConfigNoopScopes';
12
+export * from './ReactFiberConfigNoopTestSelectors';
13
+export * from './ReactFiberConfigNoopResources';
14
+export * from './ReactFiberConfigNoopSingletons';
15
+// createReactNoop will overwrite these with the mutation or persistence versions.
16
+export * from './ReactFiberConfigNoopNoMutation';
17
+export * from './ReactFiberConfigNoopNoPersistence';
18
+
19
export type HostContext = Object;
20
21
export type TextInstance = {
@@ -31,3 +40,9 @@ export type Instance = {
40
export type PublicInstance = Instance;
41
42
export type TransitionStatus = mixed;
43
+
44
+export type Container = {
45
+ rootID: string,
46
+ children: Array<Instance | TextInstance>,
47
+ pendingChildren: Array<Instance | TextInstance>,
48
+};
packages/react-noop-renderer/src/ReactFiberConfigNoopHydration.js
new
+65
@@ -0,0 +1,65 @@
1
+/**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
+
10
+// Renderers that don't support hydration
11
+// can re-export everything from this module.
12
+
13
+function shim(...args: any): empty {
14
+ throw new Error(
15
+ 'react-noop-renderer does not support hydration. ' +
16
+ 'This error is likely caused by a bug in React. ' +
17
+ 'Please file an issue.',
18
+ );
19
+}
20
+
21
+// Hydration (when unsupported)
22
+export type ActivityInstance = mixed;
23
+export type SuspenseInstance = mixed;
24
+export const supportsHydration = false;
25
+export const isSuspenseInstancePending = shim;
26
+export const isSuspenseInstanceFallback = shim;
27
+export const getSuspenseInstanceFallbackErrorDetails = shim;
28
+export const registerSuspenseInstanceRetry = shim;
29
+export const canHydrateFormStateMarker = shim;
30
+export const isFormStateMarkerMatching = shim;
31
+export const getNextHydratableSibling = shim;
32
+export const getNextHydratableSiblingAfterSingleton = shim;
33
+export const getFirstHydratableChild = shim;
34
+export const getFirstHydratableChildWithinContainer = shim;
35
+export const getFirstHydratableChildWithinActivityInstance = shim;
36
+export const getFirstHydratableChildWithinSuspenseInstance = shim;
37
+export const getFirstHydratableChildWithinSingleton = shim;
38
+export const canHydrateInstance = shim;
39
+export const canHydrateTextInstance = shim;
40
+export const canHydrateActivityInstance = shim;
41
+export const canHydrateSuspenseInstance = shim;
42
+export const hydrateInstance = shim;
43
+export const hydrateTextInstance = shim;
44
+export const hydrateActivityInstance = shim;
45
+export const hydrateSuspenseInstance = shim;
46
+export const getNextHydratableInstanceAfterActivityInstance = shim;
47
+export const getNextHydratableInstanceAfterSuspenseInstance = shim;
48
+export const finalizeHydratedChildren = shim;
49
+export const commitHydratedInstance = shim;
50
+export const commitHydratedContainer = shim;
51
+export const commitHydratedActivityInstance = shim;
52
+export const commitHydratedSuspenseInstance = shim;
53
+export const flushHydrationEvents = shim;
54
+export const clearActivityBoundary = shim;
55
+export const clearSuspenseBoundary = shim;
56
+export const clearActivityBoundaryFromContainer = shim;
57
+export const clearSuspenseBoundaryFromContainer = shim;
58
+export const hideDehydratedBoundary = shim;
59
+export const unhideDehydratedBoundary = shim;
60
+export const shouldDeleteUnhydratedTailInstances = shim;
61
+export const diffHydratedPropsForDevWarnings = shim;
62
+export const diffHydratedTextForDevWarnings = shim;
63
+export const describeHydratableInstanceForDevWarnings = shim;
64
+export const validateHydratableInstance = shim;
65
+export const validateHydratableTextInstance = shim;
packages/react-noop-renderer/src/ReactFiberConfigNoopNoMutation.js
new
+61
@@ -0,0 +1,61 @@
1
+/**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
+
10
+// Renderers that don't support mutation
11
+// can re-export everything from this module.
12
+
13
+function shim(...args: any): empty {
14
+ throw new Error(
15
+ 'This entrypoint of react-noop-renderer does not support mutation. ' +
16
+ 'This error is likely caused by a bug in React. ' +
17
+ 'Please file an issue.',
18
+ );
19
+}
20
+
21
+// Mutation (when unsupported)
22
+export const supportsMutation = false;
23
+export const cloneMutableInstance = shim;
24
+export const cloneMutableTextInstance = shim;
25
+export const appendChild = shim;
26
+export const appendChildToContainer = shim;
27
+export const commitTextUpdate = shim;
28
+export const commitMount = shim;
29
+export const commitUpdate = shim;
30
+export const insertBefore = shim;
31
+export const insertInContainerBefore = shim;
32
+export const removeChild = shim;
33
+export const removeChildFromContainer = shim;
34
+export const resetTextContent = shim;
35
+export const hideInstance = shim;
36
+export const hideTextInstance = shim;
37
+export const unhideInstance = shim;
38
+export const unhideTextInstance = shim;
39
+export const clearContainer = shim;
40
+export const applyViewTransitionName = shim;
41
+export const restoreViewTransitionName = shim;
42
+export const cancelViewTransitionName = shim;
43
+export const cancelRootViewTransitionName = shim;
44
+export const restoreRootViewTransitionName = shim;
45
+export const cloneRootViewTransitionContainer = shim;
46
+export const removeRootViewTransitionClone = shim;
47
+export type InstanceMeasurement = null;
48
+export const measureInstance = shim;
49
+export const measureClonedInstance = shim;
50
+export const wasInstanceInViewport = shim;
51
+export const hasInstanceChanged = shim;
52
+export const hasInstanceAffectedParent = shim;
53
+export const startViewTransition = shim;
54
+export type RunningViewTransition = null;
55
+export const startGestureTransition = shim;
56
+export const stopViewTransition = shim;
57
+export const addViewTransitionFinishedListener = shim;
58
+export type ViewTransitionInstance = null | {name: string, ...};
59
+export const createViewTransitionInstance = shim;
60
+export type GestureTimeline = any;
61
+export const getCurrentGestureOffset = shim;
packages/react-noop-renderer/src/ReactFiberConfigNoopNoPersistence.js
new
+29
@@ -0,0 +1,29 @@
1
+/**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
+
10
+// Renderers that don't support persistence
11
+// can re-export everything from this module.
12
+
13
+function shim(...args: any): empty {
14
+ throw new Error(
15
+ 'This entrypoint of react-noop-renderer does not support persistence. ' +
16
+ 'This error is likely caused by a bug in React. ' +
17
+ 'Please file an issue.',
18
+ );
19
+}
20
+
21
+// Persistence (when unsupported)
22
+export const supportsPersistence = false;
23
+export const cloneInstance = shim;
24
+export const createContainerChildSet = shim;
25
+export const appendChildToContainerChildSet = shim;
26
+export const finalizeContainerChildren = shim;
27
+export const replaceContainerChildren = shim;
28
+export const cloneHiddenInstance = shim;
29
+export const cloneHiddenTextInstance = shim;
packages/react-noop-renderer/src/ReactFiberConfigNoopResources.js
new
+38
@@ -0,0 +1,38 @@
1
+/**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
+
10
+// Renderers that don't support hydration
11
+// can re-export everything from this module.
12
+
13
+function shim(...args: any): empty {
14
+ throw new Error(
15
+ 'react-noop-renderer does not support Resources. ' +
16
+ 'This error is likely caused by a bug in React. ' +
17
+ 'Please file an issue.',
18
+ );
19
+}
20
+
21
+export type HoistableRoot = mixed;
22
+export type Resource = mixed;
23
+
24
+// Resources (when unsupported)
25
+export const supportsResources = false;
26
+export const isHostHoistableType = shim;
27
+export const getHoistableRoot = shim;
28
+export const getResource = shim;
29
+export const acquireResource = shim;
30
+export const releaseResource = shim;
31
+export const hydrateHoistable = shim;
32
+export const mountHoistable = shim;
33
+export const unmountHoistable = shim;
34
+export const createHoistableInstance = shim;
35
+export const prepareToCommitHoistables = shim;
36
+export const mayResourceSuspendCommit = shim;
37
+export const preloadResource = shim;
38
+export const suspendResource = shim;
packages/react-noop-renderer/src/ReactFiberConfigNoopScopes.js
new
+23
@@ -0,0 +1,23 @@
1
+/**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
+
10
+// Renderers that don't support React Scopes
11
+// can re-export everything from this module.
12
+
13
+function shim(...args: any): empty {
14
+ throw new Error(
15
+ 'react-noop-renderer does not support React Scopes. ' +
16
+ 'This error is likely caused by a bug in React. ' +
17
+ 'Please file an issue.',
18
+ );
19
+}
20
+
21
+// React Scopes (when unsupported)
22
+export const prepareScopeUpdate = shim;
23
+export const getInstanceFromScope = shim;
packages/react-noop-renderer/src/ReactFiberConfigNoopSingletons.js
new
+27
@@ -0,0 +1,27 @@
1
+/**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
+
10
+// Renderers that don't support mutation
11
+// can re-export everything from this module.
12
+
13
+function shim(...args: any): any {
14
+ throw new Error(
15
+ 'react-noop-renderer does not support Singletons. ' +
16
+ 'This error is likely caused by a bug in React. ' +
17
+ 'Please file an issue.',
18
+ );
19
+}
20
+
21
+// Resources (when unsupported)
22
+export const supportsSingletons = false;
23
+export const resolveSingletonInstance = shim;
24
+export const acquireSingletonInstance = shim;
25
+export const releaseSingletonInstance = shim;
26
+export const isHostSingletonType = shim;
27
+export const isSingletonScope = shim;
packages/react-noop-renderer/src/ReactFiberConfigNoopTestSelectors.js
new
+29
@@ -0,0 +1,29 @@
1
+/**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
+
10
+// Renderers that don't support test selectors
11
+// can re-export everything from this module.
12
+
13
+function shim(...args: any): empty {
14
+ throw new Error(
15
+ 'react-noop-renderer does not support test selectors. ' +
16
+ 'This error is likely caused by a bug in React. ' +
17
+ 'Please file an issue.',
18
+ );
19
+}
20
+
21
+// Test selectors (when unsupported)
22
+export const supportsTestSelectors = false;
23
+export const findFiberRoot = shim;
24
+export const getBoundingRect = shim;
25
+export const getTextContent = shim;
26
+export const isHiddenSubtree = shim;
27
+export const matchAccessibilityRole = shim;
28
+export const setFocusIfFocusable = shim;
29
+export const setupIntersectionObserver = shim;
packages/react-noop-renderer/src/createReactNoop.js
+301
-332
@@ -24,8 +24,12 @@ import type {RootTag} from 'react-reconciler/src/ReactRootTags';
24
import type {EventPriority} from 'react-reconciler/src/ReactEventPriorities';
25
import type {TransitionTypes} from 'react/src/ReactTransitionType';
26
import typeof * as HostConfig from 'react-reconciler/src/ReactFiberConfig';
27
+import typeof * as ReactFiberConfigWithNoMutation from 'react-reconciler/src/ReactFiberConfigWithNoMutation';
28
+import typeof * as ReactFiberConfigWithNoPersistence from 'react-reconciler/src/ReactFiberConfigWithNoPersistence';
29
+
30
import typeof * as ReconcilerAPI from 'react-reconciler/src/ReactFiberReconciler';
31
import type {
32
+ Container,
33
HostContext,
34
Instance,
35
PublicInstance,
@@ -44,17 +48,13 @@ import {
48
ConcurrentRoot,
49
LegacyRoot,
50
} from 'react-reconciler/constants';
51
+import * as DefaultConfig from './ReactFiberConfigNoop';
52
+
53
import {disableLegacyMode} from 'shared/ReactFeatureFlags';
54
55
import ReactSharedInternals from 'shared/ReactSharedInternals';
56
import ReactVersion from 'shared/ReactVersion';
57
52
-type Container = {
53
- rootID: string,
54
- children: Array<Instance | TextInstance>,
55
- pendingChildren: Array<Instance | TextInstance>,
56
- ...
57
-};
58
type Props = {
59
prop: any,
60
hidden: boolean,
@@ -107,7 +107,6 @@ if (__DEV__) {
107
Object.freeze(NO_CONTEXT);
108
}
109
110
-// $FlowFixMe[signature-verification-failure]
110
function createReactNoop(
111
reconciler: (hostConfig: HostConfig) => ReconcilerAPI,
112
useMutation: boolean,
@@ -121,12 +120,19 @@ function createReactNoop(
120
child: Instance | TextInstance,
121
): void {
122
const prevParent = child.parent;
124
- // $FlowFixMe[prop-missing]
125
- if (prevParent !== -1 && prevParent !== parentInstance.id) {
123
+
124
+ if (
125
+ prevParent !== -1 &&
126
+ prevParent !==
127
+ // $FlowFixMe[prop-missing]
128
+ (parentInstance: Instance).id
129
+ ) {
130
throw new Error('Reparenting is not allowed');
131
}
128
- // $FlowFixMe[prop-missing]
129
- child.parent = parentInstance.id;
132
+
133
+ child.parent =
134
+ // $FlowFixMe[prop-missing]
135
+ (parentInstance: Instance).id;
136
const index = parentInstance.children.indexOf(child);
137
if (index !== -1) {
138
parentInstance.children.splice(index, 1);
@@ -256,11 +262,14 @@ function createReactNoop(
262
if (__DEV__) {
263
checkPropStringCoercion(newProps.children, 'children');
264
}
259
- const clone = {
265
+ const clone: Instance = {
266
id: instance.id,
267
type: type,
268
parent: instance.parent,
263
- children: keepChildren ? instance.children : (children ?? []),
269
+ children: keepChildren
270
+ ? instance.children
271
+ : // $FlowFixMe[incompatible-type] We're not typing immutable instances.
272
+ (children ?? []),
273
text: shouldSetTextContent(type, newProps)
274
? computeText((newProps.children: any) + '', instance.context)
275
: null,
@@ -291,7 +300,6 @@ function createReactNoop(
300
enumerable: false,
301
});
302
hostCloneCounter++;
294
- // $FlowFixMe[incompatible-return]
303
return clone;
304
}
305
@@ -315,10 +323,7 @@ function createReactNoop(
323
subscriptions: Array<SuspenseyCommitSubscription> | null,
324
};
325
318
- let suspenseyThingCache: Map<
319
- SuspenseyThingRecord,
320
- 'pending' | 'fulfilled',
321
- > | null = null;
326
+ let suspenseyThingCache: Map<string, SuspenseyThingRecord> | null = null;
327
328
function startSuspendingCommit(): SuspendedState {
329
// Represents a subscription for all the suspensey things that block a
@@ -340,7 +345,7 @@ function createReactNoop(
345
// Attach a listener to the suspensey thing and create a subscription
346
// object that uses reference counting to track when all the suspensey
347
// things have loaded.
343
- // $FlowFixMe
348
+ // $FlowFixMe[incompatible-use] Still not nullable
349
const record = suspenseyThingCache.get(src);
350
if (record === undefined) {
351
throw new Error('Could not find record for key.');
@@ -352,10 +357,8 @@ function createReactNoop(
357
// Stash the subscription on the record. In `resolveSuspenseyThing`,
358
// we'll use this fire the commit once all the things have loaded.
359
if (record.subscriptions === null) {
355
- // $FlowFixMe[incompatible-use]
360
record.subscriptions = [];
361
}
358
- // $FlowFixMe[incompatible-use]
362
record.subscriptions.push(state);
363
}
364
} else {
@@ -369,10 +372,9 @@ function createReactNoop(
372
function waitForCommitToBeReady(
373
state: SuspendedState,
374
timeoutOffset: number,
372
- ): ((commit: () => mixed) => () => void) | null {
375
+ ): ((commit: () => void) => () => void) | null {
376
if (state.pendingCount > 0) {
374
- return (commit: () => mixed) => {
375
- // $FlowFixMe[incompatible-type]
377
+ return (commit: () => void) => {
378
state.commit = commit;
379
const cancelCommit = () => {
380
state.commit = null;
@@ -383,11 +385,13 @@ function createReactNoop(
385
return null;
386
}
387
386
- const sharedHostConfig = {
388
+ const sharedHostConfig: HostConfig = {
389
rendererVersion: ReactVersion,
390
rendererPackageName: 'react-noop',
391
390
- supportsSingletons: false,
392
+ ...DefaultConfig,
393
+
394
+ extraDevToolsConfig: null,
395
396
getRootHostContext() {
397
return NO_CONTEXT;
@@ -407,6 +411,8 @@ function createReactNoop(
411
return (instance: any);
412
},
413
414
+ HostTransitionContext: null,
415
+
416
createInstance(
417
type: string,
418
props: Props,
@@ -466,10 +472,6 @@ function createReactNoop(
472
return inst;
473
},
474
469
- cloneMutableInstance(instance: Instance, keepChildren: boolean): Instance {
470
- throw new Error('Not yet implemented.');
471
- },
472
-
475
appendInitialChild(
476
parentInstance: Instance,
477
child: Instance | TextInstance,
@@ -521,10 +523,6 @@ function createReactNoop(
523
return inst;
524
},
525
524
- cloneMutableTextInstance(textInstance: TextInstance): TextInstance {
525
- throw new Error('Not yet implemented.');
526
- },
527
-
526
createFragmentInstance(fragmentFiber: mixed) {
527
return null;
528
},
@@ -590,11 +588,8 @@ function createReactNoop(
588
return false;
589
},
590
593
- now: Scheduler.unstable_now,
594
-
591
isPrimaryRenderer: true,
592
warnsIfNotActing: true,
597
- supportsHydration: false,
593
594
getInstanceFromNode() {
595
throw new Error('Not yet implemented.');
@@ -612,18 +607,8 @@ function createReactNoop(
607
// NO-OP
608
},
609
615
- prepareScopeUpdate() {},
616
-
617
- getInstanceFromScope() {
618
- throw new Error('Not yet implemented.');
619
- },
620
-
610
detachDeletedInstance() {},
611
623
- logRecoverableError() {
624
- // no-op
625
- },
626
-
612
requestPostPaintCallback(callback: (time: number) => void) {
613
const endTime = Scheduler.unstable_now();
614
callback(endTime);
@@ -657,16 +642,11 @@ function createReactNoop(
642
return true;
643
},
644
660
- mayResourceSuspendCommit(resource: mixed): boolean {
661
- throw new Error(
662
- 'Resources are not implemented for React Noop yet. This method should not be called',
663
- );
664
- },
665
-
645
preloadInstance(instance: Instance, type: string, props: Props): boolean {
646
if (type !== 'suspensey-thing' || typeof props.src !== 'string') {
647
throw new Error('Attempted to preload unexpected instance: ' + type);
648
}
649
+ const src = props.src;
650
651
// In addition to preloading an instance, this method asks whether the
652
// instance is ready to be committed. If it's not, React may yield to the
@@ -675,15 +655,14 @@ function createReactNoop(
655
if (suspenseyThingCache === null) {
656
suspenseyThingCache = new Map();
657
}
678
- // $FlowFixMe
679
- const record = suspenseyThingCache.get(props.src);
658
+ const record = suspenseyThingCache.get(src);
659
if (record === undefined) {
660
const newRecord: SuspenseyThingRecord = {
661
status: 'pending',
662
subscriptions: null,
663
};
685
- // $FlowFixMe
686
- suspenseyThingCache.set(props.src, newRecord);
664
+ // $FlowFixMe[incompatible-use] Still not nullable
665
+ suspenseyThingCache.set(src, newRecord);
666
// $FlowFixMe[prop-missing]
667
const onLoadStart = props.onLoadStart;
668
if (typeof onLoadStart === 'function') {
@@ -691,26 +670,13 @@ function createReactNoop(
670
}
671
return false;
672
} else {
694
- // $FlowFixMe[prop-missing]
673
return record.status === 'fulfilled';
674
}
675
},
676
699
- preloadResource(resource: mixed): number {
700
- throw new Error(
701
- 'Resources are not implemented for React Noop yet. This method should not be called',
702
- );
703
- },
704
-
677
startSuspendingCommit,
678
suspendInstance,
679
708
- suspendResource(state: SuspendedState, resource: mixed): void {
709
- throw new Error(
710
- 'Resources are not implemented for React Noop yet. This method should not be called',
711
- );
712
- },
713
-
680
suspendOnActiveViewTransition(
681
state: SuspendedState,
682
container: Container,
@@ -741,271 +707,283 @@ function createReactNoop(
707
},
708
};
709
744
- const hostConfig: HostConfig = useMutation
745
- ? // $FlowFixMe[prop-missing]
746
- {
747
- ...sharedHostConfig,
710
+ const mutationHostConfig: Pick<
711
+ HostConfig,
712
+ $Keys<ReactFiberConfigWithNoMutation>,
713
+ > = {
714
+ supportsMutation: true,
715
749
- supportsMutation: true,
750
- supportsPersistence: false,
716
+ cloneMutableInstance() {
717
+ // required for enableGestureTransition
718
+ throw new Error('Not yet implemented.');
719
+ },
720
752
- commitMount(instance: Instance, type: string, newProps: Props): void {
753
- // Noop
754
- },
721
+ cloneMutableTextInstance() {
722
+ // required for enableGestureTransition
723
+ throw new Error('Not yet implemented.');
724
+ },
725
756
- commitUpdate(
757
- instance: Instance,
758
- type: string,
759
- oldProps: Props,
760
- newProps: Props,
761
- ): void {
762
- if (oldProps === null) {
763
- throw new Error('Should have old props');
764
- }
765
- hostUpdateCounter++;
766
- instance.prop = newProps.prop;
767
- instance.hidden = !!newProps.hidden;
726
+ commitMount(instance: Instance, type: string, newProps: Props): void {
727
+ // Noop
728
+ },
729
769
- if (type === 'suspensey-thing' && typeof newProps.src === 'string') {
770
- // $FlowFixMe[prop-missing]
771
- instance.src = newProps.src;
772
- }
730
+ commitUpdate(
731
+ instance: Instance,
732
+ type: string,
733
+ oldProps: Props,
734
+ newProps: Props,
735
+ ): void {
736
+ if (oldProps === null) {
737
+ throw new Error('Should have old props');
738
+ }
739
+ hostUpdateCounter++;
740
+ instance.prop = newProps.prop;
741
+ instance.hidden = !!newProps.hidden;
742
774
- if (shouldSetTextContent(type, newProps)) {
775
- if (__DEV__) {
776
- checkPropStringCoercion(newProps.children, 'children');
777
- }
778
- instance.text = computeText(
779
- (newProps.children: any) + '',
780
- instance.context,
781
- );
782
- }
783
- },
743
+ if (type === 'suspensey-thing' && typeof newProps.src === 'string') {
744
+ // $FlowFixMe[prop-missing]
745
+ instance.src = newProps.src;
746
+ }
747
785
- commitTextUpdate(
786
- textInstance: TextInstance,
787
- oldText: string,
788
- newText: string,
789
- ): void {
790
- hostUpdateCounter++;
791
- textInstance.text = computeText(newText, textInstance.context);
792
- },
748
+ if (shouldSetTextContent(type, newProps)) {
749
+ if (__DEV__) {
750
+ checkPropStringCoercion(newProps.children, 'children');
751
+ }
752
+ instance.text = computeText(
753
+ (newProps.children: any) + '',
754
+ instance.context,
755
+ );
756
+ }
757
+ },
758
794
- appendChild,
795
- appendChildToContainer,
796
- insertBefore,
797
- insertInContainerBefore,
798
- removeChild,
799
- removeChildFromContainer,
800
- clearContainer,
759
+ commitTextUpdate(
760
+ textInstance: TextInstance,
761
+ oldText: string,
762
+ newText: string,
763
+ ): void {
764
+ hostUpdateCounter++;
765
+ textInstance.text = computeText(newText, textInstance.context);
766
+ },
767
802
- hideInstance(instance: Instance): void {
803
- instance.hidden = true;
804
- },
768
+ appendChild,
769
+ appendChildToContainer,
770
+ insertBefore,
771
+ insertInContainerBefore,
772
+ removeChild,
773
+ removeChildFromContainer,
774
+ clearContainer,
775
806
- hideTextInstance(textInstance: TextInstance): void {
807
- textInstance.hidden = true;
808
- },
776
+ hideInstance(instance: Instance): void {
777
+ instance.hidden = true;
778
+ },
779
810
- unhideInstance(instance: Instance, props: Props): void {
811
- if (!props.hidden) {
812
- instance.hidden = false;
813
- }
814
- },
780
+ hideTextInstance(textInstance: TextInstance): void {
781
+ textInstance.hidden = true;
782
+ },
783
816
- unhideTextInstance(textInstance: TextInstance, text: string): void {
817
- textInstance.hidden = false;
818
- },
784
+ unhideInstance(instance: Instance, props: Props): void {
785
+ if (!props.hidden) {
786
+ instance.hidden = false;
787
+ }
788
+ },
789
820
- applyViewTransitionName(
821
- instance: Instance,
822
- name: string,
823
- className: ?string,
824
- ): void {},
790
+ unhideTextInstance(textInstance: TextInstance, text: string): void {
791
+ textInstance.hidden = false;
792
+ },
793
826
- restoreViewTransitionName(instance: Instance, props: Props): void {},
794
+ applyViewTransitionName(
795
+ instance: Instance,
796
+ name: string,
797
+ className: ?string,
798
+ ): void {},
799
828
- cancelViewTransitionName(
829
- instance: Instance,
830
- name: string,
831
- props: Props,
832
- ): void {},
800
+ restoreViewTransitionName(instance: Instance, props: Props): void {},
801
834
- cancelRootViewTransitionName(rootContainer: Container): void {},
802
+ cancelViewTransitionName(
803
+ instance: Instance,
804
+ name: string,
805
+ props: Props,
806
+ ): void {},
807
836
- restoreRootViewTransitionName(rootContainer: Container): void {},
808
+ cancelRootViewTransitionName(rootContainer: Container): void {},
809
838
- cloneRootViewTransitionContainer(rootContainer: Container): Instance {
839
- throw new Error('Not yet implemented.');
840
- },
810
+ restoreRootViewTransitionName(rootContainer: Container): void {},
811
842
- removeRootViewTransitionClone(
843
- rootContainer: Container,
844
- clone: Instance,
845
- ): void {
846
- throw new Error('Not implemented.');
847
- },
812
+ cloneRootViewTransitionContainer(rootContainer: Container): Instance {
813
+ throw new Error('Not yet implemented.');
814
+ },
815
849
- measureInstance(instance: Instance): InstanceMeasurement {
850
- return null;
851
- },
816
+ removeRootViewTransitionClone(
817
+ rootContainer: Container,
818
+ clone: Instance,
819
+ ): void {
820
+ throw new Error('Not implemented.');
821
+ },
822
853
- measureClonedInstance(instance: Instance): InstanceMeasurement {
854
- return null;
855
- },
823
+ measureInstance(instance: Instance): InstanceMeasurement {
824
+ return null;
825
+ },
826
857
- wasInstanceInViewport(measurement: InstanceMeasurement): boolean {
858
- return true;
859
- },
827
+ measureClonedInstance(instance: Instance): InstanceMeasurement {
828
+ return null;
829
+ },
830
861
- hasInstanceChanged(
862
- oldMeasurement: InstanceMeasurement,
863
- newMeasurement: InstanceMeasurement,
864
- ): boolean {
865
- return false;
866
- },
831
+ wasInstanceInViewport(measurement: InstanceMeasurement): boolean {
832
+ return true;
833
+ },
834
868
- hasInstanceAffectedParent(
869
- oldMeasurement: InstanceMeasurement,
870
- newMeasurement: InstanceMeasurement,
871
- ): boolean {
872
- return false;
873
- },
835
+ hasInstanceChanged(
836
+ oldMeasurement: InstanceMeasurement,
837
+ newMeasurement: InstanceMeasurement,
838
+ ): boolean {
839
+ return false;
840
+ },
841
875
- startViewTransition(
876
- rootContainer: Container,
877
- transitionTypes: null | TransitionTypes,
878
- mutationCallback: () => void,
879
- layoutCallback: () => void,
880
- afterMutationCallback: () => void,
881
- spawnedWorkCallback: () => void,
882
- passiveCallback: () => mixed,
883
- errorCallback: mixed => void,
884
- blockedCallback: string => void, // Profiling-only
885
- finishedAnimation: () => void, // Profiling-only
886
- ): null | RunningViewTransition {
887
- mutationCallback();
888
- layoutCallback();
889
- // Skip afterMutationCallback(). We don't need it since we're not animating.
890
- spawnedWorkCallback();
891
- // Skip passiveCallback(). Spawned work will schedule a task.
892
- return null;
893
- },
842
+ hasInstanceAffectedParent(
843
+ oldMeasurement: InstanceMeasurement,
844
+ newMeasurement: InstanceMeasurement,
845
+ ): boolean {
846
+ return false;
847
+ },
848
895
- startGestureTransition(
896
- rootContainer: Container,
897
- timeline: GestureTimeline,
898
- rangeStart: number,
899
- rangeEnd: number,
900
- transitionTypes: null | TransitionTypes,
901
- mutationCallback: () => void,
902
- animateCallback: () => void,
903
- errorCallback: mixed => void,
904
- ): null | RunningViewTransition {
905
- mutationCallback();
906
- animateCallback();
907
- return null;
908
- },
849
+ startViewTransition(
850
+ rootContainer: Container,
851
+ transitionTypes: null | TransitionTypes,
852
+ mutationCallback: () => void,
853
+ layoutCallback: () => void,
854
+ afterMutationCallback: () => void,
855
+ spawnedWorkCallback: () => void,
856
+ passiveCallback: () => mixed,
857
+ errorCallback: mixed => void,
858
+ blockedCallback: string => void, // Profiling-only
859
+ finishedAnimation: () => void, // Profiling-only
860
+ ): null | RunningViewTransition {
861
+ mutationCallback();
862
+ layoutCallback();
863
+ // Skip afterMutationCallback(). We don't need it since we're not animating.
864
+ spawnedWorkCallback();
865
+ // Skip passiveCallback(). Spawned work will schedule a task.
866
+ return null;
867
+ },
868
910
- stopViewTransition(transition: RunningViewTransition) {},
869
+ startGestureTransition(
870
+ rootContainer: Container,
871
+ timeline: GestureTimeline,
872
+ rangeStart: number,
873
+ rangeEnd: number,
874
+ transitionTypes: null | TransitionTypes,
875
+ mutationCallback: () => void,
876
+ animateCallback: () => void,
877
+ errorCallback: mixed => void,
878
+ ): null | RunningViewTransition {
879
+ mutationCallback();
880
+ animateCallback();
881
+ return null;
882
+ },
883
912
- addViewTransitionFinishedListener(
913
- transition: RunningViewTransition,
914
- callback: () => void,
915
- ) {
916
- callback();
917
- },
884
+ stopViewTransition(transition: RunningViewTransition) {},
885
919
- createViewTransitionInstance(name: string): ViewTransitionInstance {
920
- return null;
921
- },
886
+ addViewTransitionFinishedListener(
887
+ transition: RunningViewTransition,
888
+ callback: () => void,
889
+ ) {
890
+ callback();
891
+ },
892
923
- getCurrentGestureOffset(provider: GestureTimeline): number {
924
- return 0;
925
- },
893
+ createViewTransitionInstance(name: string): ViewTransitionInstance {
894
+ return null;
895
+ },
896
927
- resetTextContent(instance: Instance): void {
928
- instance.text = null;
929
- },
930
- }
931
- : // $FlowFixMe[prop-missing]
932
- {
933
- ...sharedHostConfig,
934
- supportsMutation: false,
935
- supportsPersistence: true,
897
+ getCurrentGestureOffset(provider: GestureTimeline): number {
898
+ return 0;
899
+ },
900
937
- cloneInstance,
938
- clearContainer,
901
+ resetTextContent(instance: Instance): void {
902
+ instance.text = null;
903
+ },
904
+ };
905
940
- createContainerChildSet(): Array<Instance | TextInstance> {
941
- return [];
942
- },
906
+ const persistenceHostConfig: Pick<
907
+ HostConfig,
908
+ $Keys<ReactFiberConfigWithNoPersistence>,
909
+ > = {
910
+ supportsPersistence: true,
911
944
- appendChildToContainerChildSet(
945
- childSet: Array<Instance | TextInstance>,
946
- child: Instance | TextInstance,
947
- ): void {
948
- childSet.push(child);
949
- },
912
+ cloneInstance,
913
951
- finalizeContainerChildren(
952
- container: Container,
953
- newChildren: Array<Instance | TextInstance>,
954
- ): void {
955
- container.pendingChildren = newChildren;
956
- if (
957
- newChildren.length === 1 &&
958
- newChildren[0].text === 'Error when completing root'
959
- ) {
960
- // Trigger an error for testing purposes
961
- throw Error('Error when completing root');
962
- }
963
- },
914
+ createContainerChildSet(): Array<Instance | TextInstance> {
915
+ return [];
916
+ },
917
965
- replaceContainerChildren(
966
- container: Container,
967
- newChildren: Array<Instance | TextInstance>,
968
- ): void {
969
- container.children = newChildren;
970
- },
918
+ appendChildToContainerChildSet(
919
+ childSet: Array<Instance | TextInstance>,
920
+ child: Instance | TextInstance,
921
+ ): void {
922
+ childSet.push(child);
923
+ },
924
972
- cloneHiddenInstance(
973
- instance: Instance,
974
- type: string,
975
- props: Props,
976
- ): Instance {
977
- const clone = cloneInstance(instance, type, props, props, true, null);
978
- clone.hidden = true;
979
- return clone;
980
- },
925
+ finalizeContainerChildren(
926
+ container: Container,
927
+ newChildren: Array<Instance | TextInstance>,
928
+ ): void {
929
+ container.pendingChildren = newChildren;
930
+ if (
931
+ newChildren.length === 1 &&
932
+ newChildren[0].text === 'Error when completing root'
933
+ ) {
934
+ // Trigger an error for testing purposes
935
+ throw Error('Error when completing root');
936
+ }
937
+ },
938
982
- cloneHiddenTextInstance(
983
- instance: TextInstance,
984
- text: string,
985
- ): TextInstance {
986
- const clone = {
987
- text: instance.text,
988
- id: instance.id,
989
- parent: instance.parent,
990
- hidden: true,
991
- context: instance.context,
992
- };
993
- // Hide from unit tests
994
- Object.defineProperty(clone, 'id', {
995
- value: clone.id,
996
- enumerable: false,
997
- });
998
- Object.defineProperty(clone, 'parent', {
999
- value: clone.parent,
1000
- enumerable: false,
1001
- });
1002
- Object.defineProperty(clone, 'context', {
1003
- value: clone.context,
1004
- enumerable: false,
1005
- });
1006
- return clone;
1007
- },
939
+ replaceContainerChildren(
940
+ container: Container,
941
+ newChildren: Array<Instance | TextInstance>,
942
+ ): void {
943
+ container.children = newChildren;
944
+ },
945
+
946
+ cloneHiddenInstance(
947
+ instance: Instance,
948
+ type: string,
949
+ props: Props,
950
+ ): Instance {
951
+ const clone = cloneInstance(instance, type, props, props, true, null);
952
+ clone.hidden = true;
953
+ return clone;
954
+ },
955
+
956
+ cloneHiddenTextInstance(
957
+ instance: TextInstance,
958
+ text: string,
959
+ ): TextInstance {
960
+ const clone = {
961
+ text: instance.text,
962
+ id: instance.id,
963
+ parent: instance.parent,
964
+ hidden: true,
965
+ context: instance.context,
966
};
967
+ // Hide from unit tests
968
+ Object.defineProperty(clone, 'id', {
969
+ value: clone.id,
970
+ enumerable: false,
971
+ });
972
+ Object.defineProperty(clone, 'parent', {
973
+ value: clone.parent,
974
+ enumerable: false,
975
+ });
976
+ Object.defineProperty(clone, 'context', {
977
+ value: clone.context,
978
+ enumerable: false,
979
+ });
980
+ return clone;
981
+ },
982
+ };
983
+
984
+ const hostConfig: HostConfig = useMutation
985
+ ? {...sharedHostConfig, ...mutationHostConfig}
986
+ : {...sharedHostConfig, ...persistenceHostConfig};
987
988
const NoopRenderer = reconciler(hostConfig);
989
@@ -1024,8 +1002,7 @@ function createReactNoop(
1002
1003
let currentEventPriority = DefaultEventPriority;
1004
1027
- // $FlowFixMe[missing-local-annot]
1028
- function createJSXElementForTestComparison(type, props) {
1005
+ function createJSXElementForTestComparison(type: mixed, props: mixed) {
1006
if (__DEV__) {
1007
const element = {
1008
type: type,
@@ -1052,8 +1029,10 @@ function createReactNoop(
1029
}
1030
}
1031
1055
- // $FlowFixMe
1056
- function childToJSX(child, text) {
1032
+ function childToJSX(
1033
+ child: null | Instance | TextInstance | Array<Instance | TextInstance>,
1034
+ text: ?string,
1035
+ ): mixed {
1036
if (text !== null) {
1037
return text;
1038
}
@@ -1108,8 +1087,7 @@ function createReactNoop(
1087
return textInstance.text;
1088
}
1089
1111
- // $FlowFixMe[missing-local-annot]
1112
- function getChildren(root) {
1090
+ function getChildren(root: ?(Container | Instance)) {
1091
if (root) {
1092
return root.children;
1093
} else {
@@ -1117,8 +1095,7 @@ function createReactNoop(
1095
}
1096
}
1097
1120
- // $FlowFixMe[missing-local-annot]
1121
- function getPendingChildren(root) {
1098
+ function getPendingChildren(root: ?(Container | Instance)) {
1099
if (root) {
1100
return root.children;
1101
} else {
@@ -1126,8 +1103,7 @@ function createReactNoop(
1103
}
1104
}
1105
1129
- // $FlowFixMe[missing-local-annot]
1130
- function getChildrenAsJSX(root) {
1106
+ function getChildrenAsJSX(root: ?(Container | Instance)) {
1107
const children = childToJSX(getChildren(root), null);
1108
if (children === null) {
1109
return null;
@@ -1138,8 +1114,7 @@ function createReactNoop(
1114
return children;
1115
}
1116
1141
- // $FlowFixMe[missing-local-annot]
1142
- function getPendingChildrenAsJSX(root) {
1117
+ function getPendingChildrenAsJSX(root: ?(Container | Instance)) {
1118
const children = childToJSX(getChildren(root), null);
1119
if (children === null) {
1120
return null;
@@ -1150,7 +1125,7 @@ function createReactNoop(
1125
return children;
1126
}
1127
1153
- function flushSync<R>(fn: () => R): R {
1128
+ function flushSync<R>(fn: () => R): ?R {
1129
if (__DEV__) {
1130
if (NoopRenderer.isAlreadyRendering()) {
1131
console.error(
@@ -1169,7 +1144,6 @@ function createReactNoop(
1144
if (fn) {
1145
return fn();
1146
} else {
1172
- // $FlowFixMe[incompatible-return]
1147
return undefined;
1148
}
1149
} finally {
@@ -1228,7 +1202,10 @@ function createReactNoop(
1202
return getPendingChildren(container);
1203
},
1204
1231
- getOrCreateRootContainer(rootID: string = DEFAULT_ROOT_ID, tag: RootTag) {
1205
+ getOrCreateRootContainer(
1206
+ rootID: string = DEFAULT_ROOT_ID,
1207
+ tag: RootTag,
1208
+ ): Container {
1209
let root = roots.get(rootID);
1210
if (!root) {
1211
const container: Container = {
@@ -1307,7 +1284,7 @@ function createReactNoop(
1284
throw new Error('createLegacyRoot: Unsupported Legacy Mode API.');
1285
}
1286
1310
- const container = {
1287
+ const container: Container = {
1288
rootID: '' + idCounter++,
1289
pendingChildren: [],
1290
children: [],
@@ -1317,9 +1294,8 @@ function createReactNoop(
1294
container,
1295
LegacyRoot,
1296
null,
1320
- // $FlowFixMe[incompatible-call] -- TODO: Discovered when typechecking noop-renderer
1321
- null,
1297
false,
1298
+ null,
1299
'',
1300
NoopRenderer.defaultOnUncaughtError,
1301
NoopRenderer.defaultOnCaughtError,
@@ -1352,8 +1328,7 @@ function createReactNoop(
1328
return getPendingChildrenAsJSX(container);
1329
},
1330
1355
- // $FlowFixMe[missing-local-annot]
1356
- getSuspenseyThingStatus(src): string | null {
1331
+ getSuspenseyThingStatus(src: string): string | null {
1332
if (suspenseyThingCache === null) {
1333
return null;
1334
} else {
@@ -1367,24 +1342,19 @@ function createReactNoop(
1342
if (suspenseyThingCache === null) {
1343
suspenseyThingCache = new Map();
1344
}
1370
- // $FlowFixMe[incompatible-call]
1345
const record = suspenseyThingCache.get(key);
1346
if (record === undefined) {
1347
const newRecord: SuspenseyThingRecord = {
1348
status: 'fulfilled',
1349
subscriptions: null,
1350
};
1377
- // $FlowFixMe
1351
+ // $FlowFixMe[incompatible-use] still non-nullable
1352
suspenseyThingCache.set(key, newRecord);
1353
} else {
1380
- // $FlowFixMe[prop-missing]
1354
if (record.status === 'pending') {
1382
- // $FlowFixMe[incompatible-use]
1355
record.status = 'fulfilled';
1384
- // $FlowFixMe[prop-missing]
1356
const subscriptions = record.subscriptions;
1357
if (subscriptions !== null) {
1387
- // $FlowFixMe[incompatible-use]
1358
record.subscriptions = null;
1359
for (let i = 0; i < subscriptions.length; i++) {
1360
const subscription = subscriptions[i];
@@ -1392,6 +1362,11 @@ function createReactNoop(
1362
if (subscription.pendingCount === 0) {
1363
const commit = subscription.commit;
1364
subscription.commit = null;
1365
+ if (commit === null) {
1366
+ throw new Error(
1367
+ 'Expected commit to be a function. This is a bug in React.',
1368
+ );
1369
+ }
1370
commit();
1371
}
1372
}
@@ -1413,7 +1388,7 @@ function createReactNoop(
1388
},
1389
1390
// Shortcut for testing a single root
1416
- render(element: React$Element<any>, callback: ?Function) {
1391
+ render(element: React$Element<any>, callback: ?Function): void {
1392
ReactNoop.renderToRootWithID(element, DEFAULT_ROOT_ID, callback);
1393
},
1394
@@ -1462,13 +1437,11 @@ function createReactNoop(
1437
return component;
1438
}
1439
if (__DEV__) {
1465
- // $FlowFixMe[incompatible-return]
1440
return NoopRenderer.findHostInstanceWithWarning(
1441
component,
1442
'findInstance',
1443
);
1444
}
1471
- // $FlowFixMe[incompatible-return]
1445
return NoopRenderer.findHostInstance(component);
1446
},
1447
@@ -1527,8 +1500,7 @@ function createReactNoop(
1500
1501
discreteUpdates: NoopRenderer.discreteUpdates,
1502
1530
- // $FlowFixMe[incompatible-return]
1531
- idleUpdates<T>(fn: () => T): T {
1503
+ idleUpdates<T>(fn: () => T): void {
1504
const prevEventPriority = currentEventPriority;
1505
currentEventPriority = IdleEventPriority;
1506
try {
@@ -1552,9 +1524,7 @@ function createReactNoop(
1524
}
1525
1526
const bufferedLog: string[] = [];
1555
- // $FlowFixMe[missing-local-annot]
1527
function log(...args: string[]) {
1557
- // $FlowFixMe[incompatible-call]
1528
bufferedLog.push(...args, '\n');
1529
}
1530
@@ -1614,8 +1584,7 @@ function createReactNoop(
1584
}
1585
}
1586
1617
- // $FlowFixMe[missing-local-annot]
1618
- function logFiber(fiber: Fiber, depth) {
1587
+ function logFiber(fiber: Fiber, depth: number) {
1588
log(
1589
' '.repeat(depth) +
1590
'- ' +
packages/react-reconciler/src/__tests__/ViewTransitionReactServer-test.js
+1
-1
@@ -39,7 +39,7 @@ describe('ViewTransitionReactServer', () => {
39
jest.restoreAllMocks();
40
});
41
42
- // @gate enableViewTransition || fb
42
+ // @gate enableViewTransition
43
it('can be rendered in React Server', async () => {
44
function App() {
45
return ReactServer.createElement(
packages/react-reconciler/src/forks/ReactFiberConfig.noop.js
-1
@@ -27,7 +27,6 @@ export * from 'react-noop-renderer/src/ReactFiberConfigNoop';
27
declare const $$$config: $FlowFixMe;
28
export opaque type Type = mixed;
29
export opaque type Props = mixed;
30
-export opaque type Container = mixed;
30
export opaque type ActivityInstance = mixed;
31
export opaque type SuspenseInstance = mixed;
32
export opaque type HydratableInstance = mixed;