diff --git a/packages/fiber/src/core/reconciler.tsx b/packages/fiber/src/core/reconciler.tsx index 2afd8b898d..bbb1f4e164 100644 --- a/packages/fiber/src/core/reconciler.tsx +++ b/packages/fiber/src/core/reconciler.tsx @@ -2,7 +2,6 @@ import * as THREE from 'three' import * as React from 'react' import Reconciler from 'react-reconciler' import { - // @ts-expect-error NoEventPriority, ContinuousEventPriority, DiscreteEventPriority, @@ -23,6 +22,103 @@ import type { RootStore } from './store' import { removeInteractivity, type EventHandlers } from './events' import type { ThreeElement } from '../three-types' +// TODO: upstream to DefinitelyTyped for React 19 +// https://github.com/facebook/react/issues/28956 +type EventPriority = number + +const createReconciler = Reconciler as unknown as < + Type, + Props, + Container, + Instance, + TextInstance, + SuspenseInstance, + HydratableInstance, + FormInstance, + PublicInstance, + HostContext, + ChildSet, + TimeoutHandle, + NoTimeout, + TransitionStatus, +>( + config: Omit< + Reconciler.HostConfig< + Type, + Props, + Container, + Instance, + TextInstance, + SuspenseInstance, + HydratableInstance, + PublicInstance, + HostContext, + null, // updatePayload + ChildSet, + TimeoutHandle, + NoTimeout + >, + 'getCurrentEventPriority' | 'prepareUpdate' | 'commitUpdate' + > & { + /** + * This method should mutate the `instance` and perform prop diffing if needed. + * + * The `internalHandle` data structure is meant to be opaque. If you bend the rules and rely on its internal fields, be aware that it may change significantly between versions. You're taking on additional maintenance risk by reading from it, and giving up all guarantees if you write something to it. + */ + commitUpdate?( + instance: Instance, + type: Type, + prevProps: Props, + nextProps: Props, + internalHandle: Reconciler.OpaqueHandle, + ): void + + // Undocumented + // https://github.com/facebook/react/pull/26722 + NotPendingTransition: TransitionStatus | null + // https://github.com/facebook/react/pull/28751 + setCurrentUpdatePriority(newPriority: EventPriority): void + getCurrentUpdatePriority(): EventPriority + resolveUpdatePriority(): EventPriority + // https://github.com/facebook/react/pull/28804 + resetFormInstance(form: FormInstance): void + // https://github.com/facebook/react/pull/25105 + requestPostPaintCallback(callback: (time: number) => void): void + // https://github.com/facebook/react/pull/26025 + shouldAttemptEagerTransition(): boolean + + /** + * This method is called during render to determine if the Host Component type and props require some kind of loading process to complete before committing an update. + */ + maySuspendCommit(type: Type, props: Props): boolean + /** + * This method may be called during render if the Host Component type and props might suspend a commit. It can be used to initiate any work that might shorten the duration of a suspended commit. + */ + preloadInstance(type: Type, props: Props): boolean + /** + * This method is called just before the commit phase. Use it to set up any necessary state while any Host Components that might suspend this commit are evaluated to determine if the commit must be suspended. + */ + startSuspendingCommit(): void + /** + * This method is called after `startSuspendingCommit` for each Host Component that indicated it might suspend a commit. + */ + suspendInstance(type: Type, props: Props): void + /** + * This method is called after all `suspendInstance` calls are complete. + * + * Return `null` if the commit can happen immediately. + * + * Return `(initiateCommit: Function) => Function` if the commit must be suspended. The argument to this callback will initiate the commit when called. The return value is a cancellation function that the Reconciler can use to abort the commit. + * + */ + waitForCommitToBeReady(): ((initiateCommit: Function) => Function) | null + }, +) => Reconciler.Reconciler + +declare module 'react-reconciler/constants' { + const NoEventPriority = 0 +} + export interface Root { fiber: Reconciler.FiberRoot store: RootStore @@ -76,12 +172,13 @@ interface HostConfig { textInstance: void suspenseInstance: Instance hydratableInstance: never + formInstance: never publicInstance: Instance['object'] hostContext: {} - updatePayload: null | [true] | [false, Instance['props']] // NOTE: removed with React 19 childSet: never timeoutHandle: number | undefined noTimeout: -1 + TransitionStatus: null } export const catalogue: Catalogue = {} @@ -324,7 +421,7 @@ const NO_CONTEXT: HostConfig['hostContext'] = {} let currentUpdatePriority: number = NoEventPriority -export const reconciler = Reconciler< +export const reconciler = createReconciler< HostConfig['type'], HostConfig['props'], HostConfig['container'], @@ -332,12 +429,13 @@ export const reconciler = Reconciler< HostConfig['textInstance'], HostConfig['suspenseInstance'], HostConfig['hydratableInstance'], + HostConfig['formInstance'], HostConfig['publicInstance'], HostConfig['hostContext'], - HostConfig['updatePayload'], HostConfig['childSet'], HostConfig['timeoutHandle'], - HostConfig['noTimeout'] + HostConfig['noTimeout'], + HostConfig['TransitionStatus'] >({ isPrimaryRenderer: false, warnsIfNotActing: false, @@ -369,7 +467,6 @@ export const reconciler = Reconciler< }, getRootHostContext: () => NO_CONTEXT, getChildHostContext: () => NO_CONTEXT, - // @ts-expect-error prepareUpdate and updatePayload removed with React 19 commitUpdate( instance: HostConfig['instance'], type: HostConfig['type'], @@ -418,8 +515,8 @@ export const reconciler = Reconciler< beforeActiveInstanceBlur() {}, afterActiveInstanceBlur() {}, detachDeletedInstance() {}, - // TODO: add shell types for these and upstream to DefinitelyTyped - // https://github.com/facebook/react/blob/main/packages/react-art/src/ReactFiberConfigART.js + prepareScopeUpdate() {}, + getInstanceFromScope: () => null, shouldAttemptEagerTransition() { return false }, @@ -464,4 +561,5 @@ export const reconciler = Reconciler< return DefaultEventPriority } }, + resetFormInstance() {}, })