@samitouri / QOS-React-1 / commits / 3bc2d41428

[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;