From 4aa96a4b1a1458601b5ce43b9447830c5ba5fd93 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Fri, 10 Sep 2021 15:02:07 -0400 Subject: [PATCH] [WIP] Delete useMutableSource implementation --- .../react-debug-tools/src/ReactDebugHooks.js | 26 +- .../ReactHooksInspectionIntegration-test.js | 37 -- packages/react-dom/src/client/ReactDOMRoot.js | 26 -- .../src/server/ReactPartialRendererHooks.js | 21 +- .../src/ReactFiberBeginWork.new.js | 18 - .../src/ReactFiberBeginWork.old.js | 18 - .../src/ReactFiberCompleteWork.new.js | 3 - .../src/ReactFiberCompleteWork.old.js | 3 - .../src/ReactFiberHooks.new.js | 403 +----------------- .../src/ReactFiberHooks.old.js | 403 +----------------- .../src/ReactFiberLane.new.js | 11 +- .../src/ReactFiberLane.old.js | 11 +- .../src/ReactFiberReconciler.new.js | 1 - .../src/ReactFiberReconciler.old.js | 1 - .../src/ReactFiberRoot.new.js | 1 - .../src/ReactFiberRoot.old.js | 1 - .../src/ReactFiberUnwindWork.new.js | 3 - .../src/ReactFiberUnwindWork.old.js | 3 - .../src/ReactInternalTypes.js | 21 +- .../src/ReactMutableSource.new.js | 108 ----- .../src/ReactMutableSource.old.js | 108 ----- packages/react-server/src/ReactFizzHooks.js | 21 +- .../react-server/src/ReactFlightServer.js | 1 - .../src/ReactSuspenseTestUtils.js | 1 - packages/react/index.classic.fb.js | 2 - packages/react/index.experimental.js | 1 - packages/react/index.js | 1 - packages/react/index.modern.fb.js | 2 - packages/react/index.stable.js | 1 - packages/react/src/React.js | 4 - packages/react/src/ReactHooks.js | 16 +- packages/react/src/ReactMutableSource.js | 34 -- .../unstable-shared-subset.experimental.js | 1 - packages/shared/ReactTypes.js | 50 --- 34 files changed, 13 insertions(+), 1349 deletions(-) delete mode 100644 packages/react-reconciler/src/ReactMutableSource.new.js delete mode 100644 packages/react-reconciler/src/ReactMutableSource.old.js delete mode 100644 packages/react/src/ReactMutableSource.js diff --git a/packages/react-debug-tools/src/ReactDebugHooks.js b/packages/react-debug-tools/src/ReactDebugHooks.js index 131a0003dcec7..b54464abc071b 100644 --- a/packages/react-debug-tools/src/ReactDebugHooks.js +++ b/packages/react-debug-tools/src/ReactDebugHooks.js @@ -7,13 +7,7 @@ * @flow */ -import type { - MutableSource, - MutableSourceGetSnapshotFn, - MutableSourceSubscribeFn, - ReactContext, - ReactProviderType, -} from 'shared/ReactTypes'; +import type {ReactContext, ReactProviderType} from 'shared/ReactTypes'; import type { Fiber, Dispatcher as DispatcherType, @@ -248,23 +242,6 @@ function useMemo( return value; } -function useMutableSource( - source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn, - subscribe: MutableSourceSubscribeFn, -): Snapshot { - // useMutableSource() composes multiple hooks internally. - // Advance the current hook index the same number of times - // so that subsequent hooks have the right memoized state. - nextHook(); // MutableSource - nextHook(); // State - nextHook(); // Effect - nextHook(); // Effect - const value = getSnapshot(source._source); - hookLog.push({primitive: 'MutableSource', stackError: new Error(), value}); - return value; -} - function useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, @@ -344,7 +321,6 @@ const Dispatcher: DispatcherType = { useRef, useState, useTransition, - useMutableSource, useSyncExternalStore, useDeferredValue, useOpaqueIdentifier, diff --git a/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js b/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js index 1c7cb75088408..588fe95c9d9a7 100644 --- a/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js +++ b/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js @@ -842,43 +842,6 @@ describe('ReactHooksInspectionIntegration', () => { ]); }); - it('should support composite useMutableSource hook', () => { - const createMutableSource = - React.createMutableSource || React.unstable_createMutableSource; - const useMutableSource = - React.useMutableSource || React.unstable_useMutableSource; - - const mutableSource = createMutableSource({}, () => 1); - function Foo(props) { - useMutableSource( - mutableSource, - () => 'snapshot', - () => {}, - ); - React.useMemo(() => 'memo', []); - return
; - } - const renderer = ReactTestRenderer.create(); - const childFiber = renderer.root.findByType(Foo)._currentFiber(); - const tree = ReactDebugTools.inspectHooksOfFiber(childFiber); - expect(tree).toEqual([ - { - id: 0, - isStateEditable: false, - name: 'MutableSource', - value: 'snapshot', - subHooks: [], - }, - { - id: 1, - isStateEditable: false, - name: 'Memo', - value: 'memo', - subHooks: [], - }, - ]); - }); - // @gate experimental || www it('should support composite useSyncExternalStore hook', () => { const useSyncExternalStore = React.unstable_useSyncExternalStore; diff --git a/packages/react-dom/src/client/ReactDOMRoot.js b/packages/react-dom/src/client/ReactDOMRoot.js index ba062fc3f71e9..c8e34547ef742 100644 --- a/packages/react-dom/src/client/ReactDOMRoot.js +++ b/packages/react-dom/src/client/ReactDOMRoot.js @@ -8,7 +8,6 @@ */ import type {Container} from './ReactDOMHostConfig'; -import type {MutableSource, ReactNodeList} from 'shared/ReactTypes'; import type {FiberRoot} from 'react-reconciler/src/ReactInternalTypes'; export type RootType = { @@ -24,7 +23,6 @@ export type CreateRootOptions = { hydrationOptions?: { onHydrated?: (suspenseNode: Comment) => void, onDeleted?: (suspenseNode: Comment) => void, - mutableSources?: Array>, ... }, // END OF TODO @@ -35,7 +33,6 @@ export type CreateRootOptions = { export type HydrateRootOptions = { // Hydration options - hydratedSources?: Array>, onHydrated?: (suspenseNode: Comment) => void, onDeleted?: (suspenseNode: Comment) => void, // Options for all roots @@ -61,7 +58,6 @@ import { createContainer, updateContainer, findHostInstanceWithNoPortals, - registerMutableSourceForHydration, } from 'react-reconciler/src/ReactFiberReconciler'; import invariant from 'shared/invariant'; import {ConcurrentRoot} from 'react-reconciler/src/ReactRootTags'; @@ -129,11 +125,6 @@ export function createRoot( const hydrate = options != null && options.hydrate === true; const hydrationCallbacks = (options != null && options.hydrationOptions) || null; - const mutableSources = - (options != null && - options.hydrationOptions != null && - options.hydrationOptions.mutableSources) || - null; // END TODO const isStrictMode = options != null && options.unstable_strictMode === true; @@ -159,15 +150,6 @@ export function createRoot( container.nodeType === COMMENT_NODE ? container.parentNode : container; listenToAllSupportedEvents(rootContainerElement); - // TODO: Delete this path - if (mutableSources) { - for (let i = 0; i < mutableSources.length; i++) { - const mutableSource = mutableSources[i]; - registerMutableSourceForHydration(root, mutableSource); - } - } - // END TODO - return new ReactDOMRoot(root); } @@ -185,7 +167,6 @@ export function hydrateRoot( // For now we reuse the whole bag of options since they contain // the hydration callbacks. const hydrationCallbacks = options != null ? options : null; - const mutableSources = (options != null && options.hydratedSources) || null; const isStrictMode = options != null && options.unstable_strictMode === true; let concurrentUpdatesByDefaultOverride = null; @@ -208,13 +189,6 @@ export function hydrateRoot( // This can't be a comment node since hydration doesn't work on comment nodes anyway. listenToAllSupportedEvents(container); - if (mutableSources) { - for (let i = 0; i < mutableSources.length; i++) { - const mutableSource = mutableSources[i]; - registerMutableSourceForHydration(root, mutableSource); - } - } - // Render the initial children updateContainer(initialChildren, root, null, null); diff --git a/packages/react-dom/src/server/ReactPartialRendererHooks.js b/packages/react-dom/src/server/ReactPartialRendererHooks.js index 0712d9fee9285..6fec5149026e0 100644 --- a/packages/react-dom/src/server/ReactPartialRendererHooks.js +++ b/packages/react-dom/src/server/ReactPartialRendererHooks.js @@ -9,12 +9,7 @@ import type {Dispatcher as DispatcherType} from 'react-reconciler/src/ReactInternalTypes'; -import type { - MutableSource, - MutableSourceGetSnapshotFn, - MutableSourceSubscribeFn, - ReactContext, -} from 'shared/ReactTypes'; +import type {ReactContext} from 'shared/ReactTypes'; import type PartialRenderer from './ReactPartialRenderer'; import {validateContextBounds} from './ReactPartialRendererContext'; @@ -450,18 +445,6 @@ export function useCallback( return useMemo(() => callback, deps); } -// TODO Decide on how to implement this hook for server rendering. -// If a mutation occurs during render, consider triggering a Suspense boundary -// and falling back to client rendering. -function useMutableSource( - source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn, - subscribe: MutableSourceSubscribeFn, -): Snapshot { - resolveCurrentlyRenderingComponent(); - return getSnapshot(source._source); -} - function useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, @@ -519,8 +502,6 @@ export const Dispatcher: DispatcherType = { useDeferredValue, useTransition, useOpaqueIdentifier, - // Subscriptions are not setup in a server environment. - useMutableSource, useSyncExternalStore, }; diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.new.js b/packages/react-reconciler/src/ReactFiberBeginWork.new.js index d9874bba34119..894bc523e4b58 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.new.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.new.js @@ -12,7 +12,6 @@ import type {LazyComponent as LazyComponentType} from 'react/src/ReactLazy'; import type {Fiber, FiberRoot} from './ReactInternalTypes'; import type {TypeOfMode} from './ReactTypeOfMode'; import type {Lanes, Lane} from './ReactFiberLane.new'; -import type {MutableSource} from 'shared/ReactTypes'; import type { SuspenseState, SuspenseListRenderState, @@ -145,7 +144,6 @@ import { isSuspenseInstancePending, isSuspenseInstanceFallback, registerSuspenseInstanceRetry, - supportsHydration, isPrimaryRenderer, supportsPersistence, getOffscreenContainerProps, @@ -224,7 +222,6 @@ import { RetryAfterError, NoContext, } from './ReactFiberWorkLoop.new'; -import {setWorkInProgressVersion} from './ReactMutableSource.new'; import { requestCacheFromPool, pushCacheProvider, @@ -1303,21 +1300,6 @@ function updateHostRoot(current, workInProgress, renderLanes) { // We always try to hydrate. If this isn't a hydration pass there won't // be any children to hydrate which is effectively the same thing as // not hydrating. - - if (supportsHydration) { - const mutableSourceEagerHydrationData = - root.mutableSourceEagerHydrationData; - if (mutableSourceEagerHydrationData != null) { - for (let i = 0; i < mutableSourceEagerHydrationData.length; i += 2) { - const mutableSource = ((mutableSourceEagerHydrationData[ - i - ]: any): MutableSource); - const version = mutableSourceEagerHydrationData[i + 1]; - setWorkInProgressVersion(mutableSource, version); - } - } - } - const child = mountChildFibers( workInProgress, null, diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.old.js b/packages/react-reconciler/src/ReactFiberBeginWork.old.js index 74037fbe966f2..264e723b28973 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.old.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.old.js @@ -12,7 +12,6 @@ import type {LazyComponent as LazyComponentType} from 'react/src/ReactLazy'; import type {Fiber, FiberRoot} from './ReactInternalTypes'; import type {TypeOfMode} from './ReactTypeOfMode'; import type {Lanes, Lane} from './ReactFiberLane.old'; -import type {MutableSource} from 'shared/ReactTypes'; import type { SuspenseState, SuspenseListRenderState, @@ -145,7 +144,6 @@ import { isSuspenseInstancePending, isSuspenseInstanceFallback, registerSuspenseInstanceRetry, - supportsHydration, isPrimaryRenderer, supportsPersistence, getOffscreenContainerProps, @@ -224,7 +222,6 @@ import { RetryAfterError, NoContext, } from './ReactFiberWorkLoop.old'; -import {setWorkInProgressVersion} from './ReactMutableSource.old'; import { requestCacheFromPool, pushCacheProvider, @@ -1303,21 +1300,6 @@ function updateHostRoot(current, workInProgress, renderLanes) { // We always try to hydrate. If this isn't a hydration pass there won't // be any children to hydrate which is effectively the same thing as // not hydrating. - - if (supportsHydration) { - const mutableSourceEagerHydrationData = - root.mutableSourceEagerHydrationData; - if (mutableSourceEagerHydrationData != null) { - for (let i = 0; i < mutableSourceEagerHydrationData.length; i += 2) { - const mutableSource = ((mutableSourceEagerHydrationData[ - i - ]: any): MutableSource); - const version = mutableSourceEagerHydrationData[i + 1]; - setWorkInProgressVersion(mutableSource, version); - } - } - } - const child = mountChildFibers( workInProgress, null, diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.new.js b/packages/react-reconciler/src/ReactFiberCompleteWork.new.js index 1382439f1ee7a..e7d6f40e98bdb 100644 --- a/packages/react-reconciler/src/ReactFiberCompleteWork.new.js +++ b/packages/react-reconciler/src/ReactFiberCompleteWork.new.js @@ -30,8 +30,6 @@ import type {SuspenseContext} from './ReactFiberSuspenseContext.new'; import type {OffscreenState} from './ReactFiberOffscreenComponent'; import type {Cache, SpawnedCachePool} from './ReactFiberCacheComponent.new'; -import {resetWorkInProgressVersions as resetMutableSourceWorkInProgressVersions} from './ReactMutableSource.new'; - import {now} from './Scheduler'; import { @@ -854,7 +852,6 @@ function completeWork( } popHostContainer(workInProgress); popTopLevelLegacyContextObject(workInProgress); - resetMutableSourceWorkInProgressVersions(); if (fiberRoot.pendingContext) { fiberRoot.context = fiberRoot.pendingContext; fiberRoot.pendingContext = null; diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.old.js b/packages/react-reconciler/src/ReactFiberCompleteWork.old.js index 746e3d4b572da..6e8406fc05dfd 100644 --- a/packages/react-reconciler/src/ReactFiberCompleteWork.old.js +++ b/packages/react-reconciler/src/ReactFiberCompleteWork.old.js @@ -30,8 +30,6 @@ import type {SuspenseContext} from './ReactFiberSuspenseContext.old'; import type {OffscreenState} from './ReactFiberOffscreenComponent'; import type {Cache, SpawnedCachePool} from './ReactFiberCacheComponent.old'; -import {resetWorkInProgressVersions as resetMutableSourceWorkInProgressVersions} from './ReactMutableSource.old'; - import {now} from './Scheduler'; import { @@ -854,7 +852,6 @@ function completeWork( } popHostContainer(workInProgress); popTopLevelLegacyContextObject(workInProgress); - resetMutableSourceWorkInProgressVersions(); if (fiberRoot.pendingContext) { fiberRoot.context = fiberRoot.pendingContext; fiberRoot.pendingContext = null; diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index 64a4b446ba516..f55946f0f7e18 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -7,16 +7,10 @@ * @flow */ -import type { - MutableSource, - MutableSourceGetSnapshotFn, - MutableSourceSubscribeFn, - ReactContext, -} from 'shared/ReactTypes'; +import type {ReactContext} from 'shared/ReactTypes'; import type {Fiber, Dispatcher, HookType} from './ReactInternalTypes'; import type {Lanes, Lane} from './ReactFiberLane.new'; import type {HookFlags} from './ReactHookEffectTags'; -import type {FiberRoot} from './ReactInternalTypes'; import type {OpaqueIDType} from './ReactFiberHostConfig'; import type {Cache} from './ReactFiberCacheComponent.new'; import type {Flags} from './ReactFiberFlags'; @@ -49,7 +43,6 @@ import { intersectLanes, isTransitionLane, markRootEntangled, - markRootMutableRead, NoTimestamp, } from './ReactFiberLane.new'; import { @@ -75,7 +68,6 @@ import { Passive as HookPassive, } from './ReactHookEffectTags'; import { - getWorkInProgressRoot, scheduleUpdateOnFiber, requestUpdateLane, requestEventTime, @@ -99,12 +91,6 @@ import { makeClientIdInDEV, makeOpaqueHydratingObject, } from './ReactFiberHostConfig'; -import { - getWorkInProgressVersion, - markSourceAsDirty, - setWorkInProgressVersion, - warnAboutMultipleRenderersDEV, -} from './ReactMutableSource.new'; import {getIsRendering} from './ReactCurrentFiber'; import {logStateUpdateScheduled} from './DebugTracing'; import {markStateUpdateScheduled} from './SchedulingProfiler'; @@ -115,7 +101,6 @@ import { entangleTransitions, } from './ReactUpdateQueue.new'; import {pushInterleavedQueue} from './ReactFiberInterleavedUpdates.new'; -import {getIsStrictModeForDevtools} from './ReactFiberReconciler.new'; import {warnOnSubscriptionInsideStartTransition} from 'shared/ReactFeatureFlags'; const {ReactCurrentDispatcher, ReactCurrentBatchConfig} = ReactSharedInternals; @@ -936,322 +921,6 @@ function rerenderReducer( return [newState, dispatch]; } -type MutableSourceMemoizedState = {| - refs: { - getSnapshot: MutableSourceGetSnapshotFn, - setSnapshot: Snapshot => void, - }, - source: MutableSource, - subscribe: MutableSourceSubscribeFn, -|}; - -function readFromUnsubscribedMutableSource( - root: FiberRoot, - source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn, -): Snapshot { - if (__DEV__) { - warnAboutMultipleRenderersDEV(source); - } - - const getVersion = source._getVersion; - const version = getVersion(source._source); - - // Is it safe for this component to read from this source during the current render? - let isSafeToReadFromSource = false; - - // Check the version first. - // If this render has already been started with a specific version, - // we can use it alone to determine if we can safely read from the source. - const currentRenderVersion = getWorkInProgressVersion(source); - if (currentRenderVersion !== null) { - // It's safe to read if the store hasn't been mutated since the last time - // we read something. - isSafeToReadFromSource = currentRenderVersion === version; - } else { - // If there's no version, then this is the first time we've read from the - // source during the current render pass, so we need to do a bit more work. - // What we need to determine is if there are any hooks that already - // subscribed to the source, and if so, whether there are any pending - // mutations that haven't been synchronized yet. - // - // If there are no pending mutations, then `root.mutableReadLanes` will be - // empty, and we know we can safely read. - // - // If there *are* pending mutations, we may still be able to safely read - // if the currently rendering lanes are inclusive of the pending mutation - // lanes, since that guarantees that the value we're about to read from - // the source is consistent with the values that we read during the most - // recent mutation. - isSafeToReadFromSource = isSubsetOfLanes( - renderLanes, - root.mutableReadLanes, - ); - - if (isSafeToReadFromSource) { - // If it's safe to read from this source during the current render, - // store the version in case other components read from it. - // A changed version number will let those components know to throw and restart the render. - setWorkInProgressVersion(source, version); - } - } - - if (isSafeToReadFromSource) { - const snapshot = getSnapshot(source._source); - if (__DEV__) { - if (typeof snapshot === 'function') { - console.error( - 'Mutable source should not return a function as the snapshot value. ' + - 'Functions may close over mutable values and cause tearing.', - ); - } - } - return snapshot; - } else { - // This handles the special case of a mutable source being shared between renderers. - // In that case, if the source is mutated between the first and second renderer, - // The second renderer don't know that it needs to reset the WIP version during unwind, - // (because the hook only marks sources as dirty if it's written to their WIP version). - // That would cause this tear check to throw again and eventually be visible to the user. - // We can avoid this infinite loop by explicitly marking the source as dirty. - // - // This can lead to tearing in the first renderer when it resumes, - // but there's nothing we can do about that (short of throwing here and refusing to continue the render). - markSourceAsDirty(source); - - // Intentioally throw an error to force React to retry synchronously. During - // the synchronous retry, it will block interleaved mutations, so we should - // get a consistent read. Therefore, the following error should never be - // visible to the user. - // - // If it were to become visible to the user, it suggests one of two things: - // a bug in React, or (more likely), a mutation during the render phase that - // caused the second re-render attempt to be different from the first. - // - // We know it's the second case if the logs are currently disabled. So in - // dev, we can present a more accurate error message. - if (__DEV__) { - // eslint-disable-next-line react-internal/no-production-logging - if (getIsStrictModeForDevtools()) { - // If getIsStrictModeForDevtools is true, this is the dev-only double render - // This is only reachable if there was a mutation during render. Show a helpful - // error message. - // - // Something interesting to note: because we only double render in - // development, this error will never happen during production. This is - // actually true of all errors that occur during a double render, - // because if the first render had thrown, we would have exited the - // begin phase without double rendering. We should consider suppressing - // any error from a double render (with a warning) to more closely match - // the production behavior. - const componentName = getComponentNameFromFiber( - currentlyRenderingFiber, - ); - invariant( - false, - 'A mutable source was mutated while the %s component was rendering. ' + - 'This is not supported. Move any mutations into event handlers ' + - 'or effects.', - componentName, - ); - } - } - - // We expect this error not to be thrown during the synchronous retry, - // because we blocked interleaved mutations. - invariant( - false, - 'Cannot read from mutable source during the current render without tearing. This may be a bug in React. Please file an issue.', - ); - } -} - -function useMutableSource( - hook: Hook, - source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn, - subscribe: MutableSourceSubscribeFn, -): Snapshot { - const root = ((getWorkInProgressRoot(): any): FiberRoot); - invariant( - root !== null, - 'Expected a work-in-progress root. This is a bug in React. Please file an issue.', - ); - - const getVersion = source._getVersion; - const version = getVersion(source._source); - - const dispatcher = ReactCurrentDispatcher.current; - - // eslint-disable-next-line prefer-const - let [currentSnapshot, setSnapshot] = dispatcher.useState(() => - readFromUnsubscribedMutableSource(root, source, getSnapshot), - ); - let snapshot = currentSnapshot; - - // Grab a handle to the state hook as well. - // We use it to clear the pending update queue if we have a new source. - const stateHook = ((workInProgressHook: any): Hook); - - const memoizedState = ((hook.memoizedState: any): MutableSourceMemoizedState< - Source, - Snapshot, - >); - const refs = memoizedState.refs; - const prevGetSnapshot = refs.getSnapshot; - const prevSource = memoizedState.source; - const prevSubscribe = memoizedState.subscribe; - - const fiber = currentlyRenderingFiber; - - hook.memoizedState = ({ - refs, - source, - subscribe, - }: MutableSourceMemoizedState); - - // Sync the values needed by our subscription handler after each commit. - dispatcher.useEffect(() => { - refs.getSnapshot = getSnapshot; - - // Normally the dispatch function for a state hook never changes, - // but this hook recreates the queue in certain cases to avoid updates from stale sources. - // handleChange() below needs to reference the dispatch function without re-subscribing, - // so we use a ref to ensure that it always has the latest version. - refs.setSnapshot = setSnapshot; - - // Check for a possible change between when we last rendered now. - const maybeNewVersion = getVersion(source._source); - if (!is(version, maybeNewVersion)) { - const maybeNewSnapshot = getSnapshot(source._source); - if (__DEV__) { - if (typeof maybeNewSnapshot === 'function') { - console.error( - 'Mutable source should not return a function as the snapshot value. ' + - 'Functions may close over mutable values and cause tearing.', - ); - } - } - - if (!is(snapshot, maybeNewSnapshot)) { - setSnapshot(maybeNewSnapshot); - - const lane = requestUpdateLane(fiber); - markRootMutableRead(root, lane); - } - // If the source mutated between render and now, - // there may be state updates already scheduled from the old source. - // Entangle the updates so that they render in the same batch. - markRootEntangled(root, root.mutableReadLanes); - } - }, [getSnapshot, source, subscribe]); - - // If we got a new source or subscribe function, re-subscribe in a passive effect. - dispatcher.useEffect(() => { - const handleChange = () => { - const latestGetSnapshot = refs.getSnapshot; - const latestSetSnapshot = refs.setSnapshot; - - try { - latestSetSnapshot(latestGetSnapshot(source._source)); - - // Record a pending mutable source update with the same expiration time. - const lane = requestUpdateLane(fiber); - - markRootMutableRead(root, lane); - } catch (error) { - // A selector might throw after a source mutation. - // e.g. it might try to read from a part of the store that no longer exists. - // In this case we should still schedule an update with React. - // Worst case the selector will throw again and then an error boundary will handle it. - latestSetSnapshot( - (() => { - throw error; - }: any), - ); - } - }; - - const unsubscribe = subscribe(source._source, handleChange); - if (__DEV__) { - if (typeof unsubscribe !== 'function') { - console.error( - 'Mutable source subscribe function must return an unsubscribe function.', - ); - } - } - - return unsubscribe; - }, [source, subscribe]); - - // If any of the inputs to useMutableSource change, reading is potentially unsafe. - // - // If either the source or the subscription have changed we can't can't trust the update queue. - // Maybe the source changed in a way that the old subscription ignored but the new one depends on. - // - // If the getSnapshot function changed, we also shouldn't rely on the update queue. - // It's possible that the underlying source was mutated between the when the last "change" event fired, - // and when the current render (with the new getSnapshot function) is processed. - // - // In both cases, we need to throw away pending updates (since they are no longer relevant) - // and treat reading from the source as we do in the mount case. - if ( - !is(prevGetSnapshot, getSnapshot) || - !is(prevSource, source) || - !is(prevSubscribe, subscribe) - ) { - // Create a new queue and setState method, - // So if there are interleaved updates, they get pushed to the older queue. - // When this becomes current, the previous queue and dispatch method will be discarded, - // including any interleaving updates that occur. - const newQueue: UpdateQueue> = { - pending: null, - interleaved: null, - lanes: NoLanes, - dispatch: null, - lastRenderedReducer: basicStateReducer, - lastRenderedState: snapshot, - }; - newQueue.dispatch = setSnapshot = (dispatchAction.bind( - null, - currentlyRenderingFiber, - newQueue, - ): any); - stateHook.queue = newQueue; - stateHook.baseQueue = null; - snapshot = readFromUnsubscribedMutableSource(root, source, getSnapshot); - stateHook.memoizedState = stateHook.baseState = snapshot; - } - - return snapshot; -} - -function mountMutableSource( - source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn, - subscribe: MutableSourceSubscribeFn, -): Snapshot { - const hook = mountWorkInProgressHook(); - hook.memoizedState = ({ - refs: { - getSnapshot, - setSnapshot: (null: any), - }, - source, - subscribe, - }: MutableSourceMemoizedState); - return useMutableSource(hook, source, getSnapshot, subscribe); -} - -function updateMutableSource( - source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn, - subscribe: MutableSourceSubscribeFn, -): Snapshot { - const hook = updateWorkInProgressHook(); - return useMutableSource(hook, source, getSnapshot, subscribe); -} - function mountSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, @@ -2231,7 +1900,6 @@ export const ContextOnlyDispatcher: Dispatcher = { useDebugValue: throwInvalidHookError, useDeferredValue: throwInvalidHookError, useTransition: throwInvalidHookError, - useMutableSource: throwInvalidHookError, useSyncExternalStore: throwInvalidHookError, useOpaqueIdentifier: throwInvalidHookError, @@ -2257,7 +1925,6 @@ const HooksDispatcherOnMount: Dispatcher = { useDebugValue: mountDebugValue, useDeferredValue: mountDeferredValue, useTransition: mountTransition, - useMutableSource: mountMutableSource, useSyncExternalStore: mountSyncExternalStore, useOpaqueIdentifier: mountOpaqueIdentifier, @@ -2283,7 +1950,6 @@ const HooksDispatcherOnUpdate: Dispatcher = { useDebugValue: updateDebugValue, useDeferredValue: updateDeferredValue, useTransition: updateTransition, - useMutableSource: updateMutableSource, useSyncExternalStore: updateSyncExternalStore, useOpaqueIdentifier: updateOpaqueIdentifier, @@ -2309,7 +1975,6 @@ const HooksDispatcherOnRerender: Dispatcher = { useDebugValue: updateDebugValue, useDeferredValue: rerenderDeferredValue, useTransition: rerenderTransition, - useMutableSource: updateMutableSource, useSyncExternalStore: mountSyncExternalStore, useOpaqueIdentifier: rerenderOpaqueIdentifier, @@ -2450,15 +2115,6 @@ if (__DEV__) { mountHookTypesDev(); return mountTransition(); }, - useMutableSource( - source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn, - subscribe: MutableSourceSubscribeFn, - ): Snapshot { - currentHookNameInDev = 'useMutableSource'; - mountHookTypesDev(); - return mountMutableSource(source, getSnapshot, subscribe); - }, useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, @@ -2582,15 +2238,6 @@ if (__DEV__) { updateHookTypesDev(); return mountTransition(); }, - useMutableSource( - source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn, - subscribe: MutableSourceSubscribeFn, - ): Snapshot { - currentHookNameInDev = 'useMutableSource'; - updateHookTypesDev(); - return mountMutableSource(source, getSnapshot, subscribe); - }, useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, @@ -2714,15 +2361,6 @@ if (__DEV__) { updateHookTypesDev(); return updateTransition(); }, - useMutableSource( - source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn, - subscribe: MutableSourceSubscribeFn, - ): Snapshot { - currentHookNameInDev = 'useMutableSource'; - updateHookTypesDev(); - return updateMutableSource(source, getSnapshot, subscribe); - }, useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, @@ -2847,15 +2485,6 @@ if (__DEV__) { updateHookTypesDev(); return rerenderTransition(); }, - useMutableSource( - source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn, - subscribe: MutableSourceSubscribeFn, - ): Snapshot { - currentHookNameInDev = 'useMutableSource'; - updateHookTypesDev(); - return updateMutableSource(source, getSnapshot, subscribe); - }, useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, @@ -2992,16 +2621,6 @@ if (__DEV__) { mountHookTypesDev(); return mountTransition(); }, - useMutableSource( - source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn, - subscribe: MutableSourceSubscribeFn, - ): Snapshot { - currentHookNameInDev = 'useMutableSource'; - warnInvalidHookAccess(); - mountHookTypesDev(); - return mountMutableSource(source, getSnapshot, subscribe); - }, useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, @@ -3140,16 +2759,6 @@ if (__DEV__) { updateHookTypesDev(); return updateTransition(); }, - useMutableSource( - source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn, - subscribe: MutableSourceSubscribeFn, - ): Snapshot { - currentHookNameInDev = 'useMutableSource'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateMutableSource(source, getSnapshot, subscribe); - }, useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, @@ -3289,16 +2898,6 @@ if (__DEV__) { updateHookTypesDev(); return rerenderTransition(); }, - useMutableSource( - source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn, - subscribe: MutableSourceSubscribeFn, - ): Snapshot { - currentHookNameInDev = 'useMutableSource'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateMutableSource(source, getSnapshot, subscribe); - }, useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js index 11573e3b5e0a3..bc8d5d97e2c6c 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.old.js +++ b/packages/react-reconciler/src/ReactFiberHooks.old.js @@ -7,16 +7,10 @@ * @flow */ -import type { - MutableSource, - MutableSourceGetSnapshotFn, - MutableSourceSubscribeFn, - ReactContext, -} from 'shared/ReactTypes'; +import type {ReactContext} from 'shared/ReactTypes'; import type {Fiber, Dispatcher, HookType} from './ReactInternalTypes'; import type {Lanes, Lane} from './ReactFiberLane.old'; import type {HookFlags} from './ReactHookEffectTags'; -import type {FiberRoot} from './ReactInternalTypes'; import type {OpaqueIDType} from './ReactFiberHostConfig'; import type {Cache} from './ReactFiberCacheComponent.old'; import type {Flags} from './ReactFiberFlags'; @@ -49,7 +43,6 @@ import { intersectLanes, isTransitionLane, markRootEntangled, - markRootMutableRead, NoTimestamp, } from './ReactFiberLane.old'; import { @@ -75,7 +68,6 @@ import { Passive as HookPassive, } from './ReactHookEffectTags'; import { - getWorkInProgressRoot, scheduleUpdateOnFiber, requestUpdateLane, requestEventTime, @@ -99,12 +91,6 @@ import { makeClientIdInDEV, makeOpaqueHydratingObject, } from './ReactFiberHostConfig'; -import { - getWorkInProgressVersion, - markSourceAsDirty, - setWorkInProgressVersion, - warnAboutMultipleRenderersDEV, -} from './ReactMutableSource.old'; import {getIsRendering} from './ReactCurrentFiber'; import {logStateUpdateScheduled} from './DebugTracing'; import {markStateUpdateScheduled} from './SchedulingProfiler'; @@ -115,7 +101,6 @@ import { entangleTransitions, } from './ReactUpdateQueue.old'; import {pushInterleavedQueue} from './ReactFiberInterleavedUpdates.old'; -import {getIsStrictModeForDevtools} from './ReactFiberReconciler.old'; import {warnOnSubscriptionInsideStartTransition} from 'shared/ReactFeatureFlags'; const {ReactCurrentDispatcher, ReactCurrentBatchConfig} = ReactSharedInternals; @@ -936,322 +921,6 @@ function rerenderReducer( return [newState, dispatch]; } -type MutableSourceMemoizedState = {| - refs: { - getSnapshot: MutableSourceGetSnapshotFn, - setSnapshot: Snapshot => void, - }, - source: MutableSource, - subscribe: MutableSourceSubscribeFn, -|}; - -function readFromUnsubscribedMutableSource( - root: FiberRoot, - source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn, -): Snapshot { - if (__DEV__) { - warnAboutMultipleRenderersDEV(source); - } - - const getVersion = source._getVersion; - const version = getVersion(source._source); - - // Is it safe for this component to read from this source during the current render? - let isSafeToReadFromSource = false; - - // Check the version first. - // If this render has already been started with a specific version, - // we can use it alone to determine if we can safely read from the source. - const currentRenderVersion = getWorkInProgressVersion(source); - if (currentRenderVersion !== null) { - // It's safe to read if the store hasn't been mutated since the last time - // we read something. - isSafeToReadFromSource = currentRenderVersion === version; - } else { - // If there's no version, then this is the first time we've read from the - // source during the current render pass, so we need to do a bit more work. - // What we need to determine is if there are any hooks that already - // subscribed to the source, and if so, whether there are any pending - // mutations that haven't been synchronized yet. - // - // If there are no pending mutations, then `root.mutableReadLanes` will be - // empty, and we know we can safely read. - // - // If there *are* pending mutations, we may still be able to safely read - // if the currently rendering lanes are inclusive of the pending mutation - // lanes, since that guarantees that the value we're about to read from - // the source is consistent with the values that we read during the most - // recent mutation. - isSafeToReadFromSource = isSubsetOfLanes( - renderLanes, - root.mutableReadLanes, - ); - - if (isSafeToReadFromSource) { - // If it's safe to read from this source during the current render, - // store the version in case other components read from it. - // A changed version number will let those components know to throw and restart the render. - setWorkInProgressVersion(source, version); - } - } - - if (isSafeToReadFromSource) { - const snapshot = getSnapshot(source._source); - if (__DEV__) { - if (typeof snapshot === 'function') { - console.error( - 'Mutable source should not return a function as the snapshot value. ' + - 'Functions may close over mutable values and cause tearing.', - ); - } - } - return snapshot; - } else { - // This handles the special case of a mutable source being shared between renderers. - // In that case, if the source is mutated between the first and second renderer, - // The second renderer don't know that it needs to reset the WIP version during unwind, - // (because the hook only marks sources as dirty if it's written to their WIP version). - // That would cause this tear check to throw again and eventually be visible to the user. - // We can avoid this infinite loop by explicitly marking the source as dirty. - // - // This can lead to tearing in the first renderer when it resumes, - // but there's nothing we can do about that (short of throwing here and refusing to continue the render). - markSourceAsDirty(source); - - // Intentioally throw an error to force React to retry synchronously. During - // the synchronous retry, it will block interleaved mutations, so we should - // get a consistent read. Therefore, the following error should never be - // visible to the user. - // - // If it were to become visible to the user, it suggests one of two things: - // a bug in React, or (more likely), a mutation during the render phase that - // caused the second re-render attempt to be different from the first. - // - // We know it's the second case if the logs are currently disabled. So in - // dev, we can present a more accurate error message. - if (__DEV__) { - // eslint-disable-next-line react-internal/no-production-logging - if (getIsStrictModeForDevtools()) { - // If getIsStrictModeForDevtools is true, this is the dev-only double render - // This is only reachable if there was a mutation during render. Show a helpful - // error message. - // - // Something interesting to note: because we only double render in - // development, this error will never happen during production. This is - // actually true of all errors that occur during a double render, - // because if the first render had thrown, we would have exited the - // begin phase without double rendering. We should consider suppressing - // any error from a double render (with a warning) to more closely match - // the production behavior. - const componentName = getComponentNameFromFiber( - currentlyRenderingFiber, - ); - invariant( - false, - 'A mutable source was mutated while the %s component was rendering. ' + - 'This is not supported. Move any mutations into event handlers ' + - 'or effects.', - componentName, - ); - } - } - - // We expect this error not to be thrown during the synchronous retry, - // because we blocked interleaved mutations. - invariant( - false, - 'Cannot read from mutable source during the current render without tearing. This may be a bug in React. Please file an issue.', - ); - } -} - -function useMutableSource( - hook: Hook, - source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn, - subscribe: MutableSourceSubscribeFn, -): Snapshot { - const root = ((getWorkInProgressRoot(): any): FiberRoot); - invariant( - root !== null, - 'Expected a work-in-progress root. This is a bug in React. Please file an issue.', - ); - - const getVersion = source._getVersion; - const version = getVersion(source._source); - - const dispatcher = ReactCurrentDispatcher.current; - - // eslint-disable-next-line prefer-const - let [currentSnapshot, setSnapshot] = dispatcher.useState(() => - readFromUnsubscribedMutableSource(root, source, getSnapshot), - ); - let snapshot = currentSnapshot; - - // Grab a handle to the state hook as well. - // We use it to clear the pending update queue if we have a new source. - const stateHook = ((workInProgressHook: any): Hook); - - const memoizedState = ((hook.memoizedState: any): MutableSourceMemoizedState< - Source, - Snapshot, - >); - const refs = memoizedState.refs; - const prevGetSnapshot = refs.getSnapshot; - const prevSource = memoizedState.source; - const prevSubscribe = memoizedState.subscribe; - - const fiber = currentlyRenderingFiber; - - hook.memoizedState = ({ - refs, - source, - subscribe, - }: MutableSourceMemoizedState); - - // Sync the values needed by our subscription handler after each commit. - dispatcher.useEffect(() => { - refs.getSnapshot = getSnapshot; - - // Normally the dispatch function for a state hook never changes, - // but this hook recreates the queue in certain cases to avoid updates from stale sources. - // handleChange() below needs to reference the dispatch function without re-subscribing, - // so we use a ref to ensure that it always has the latest version. - refs.setSnapshot = setSnapshot; - - // Check for a possible change between when we last rendered now. - const maybeNewVersion = getVersion(source._source); - if (!is(version, maybeNewVersion)) { - const maybeNewSnapshot = getSnapshot(source._source); - if (__DEV__) { - if (typeof maybeNewSnapshot === 'function') { - console.error( - 'Mutable source should not return a function as the snapshot value. ' + - 'Functions may close over mutable values and cause tearing.', - ); - } - } - - if (!is(snapshot, maybeNewSnapshot)) { - setSnapshot(maybeNewSnapshot); - - const lane = requestUpdateLane(fiber); - markRootMutableRead(root, lane); - } - // If the source mutated between render and now, - // there may be state updates already scheduled from the old source. - // Entangle the updates so that they render in the same batch. - markRootEntangled(root, root.mutableReadLanes); - } - }, [getSnapshot, source, subscribe]); - - // If we got a new source or subscribe function, re-subscribe in a passive effect. - dispatcher.useEffect(() => { - const handleChange = () => { - const latestGetSnapshot = refs.getSnapshot; - const latestSetSnapshot = refs.setSnapshot; - - try { - latestSetSnapshot(latestGetSnapshot(source._source)); - - // Record a pending mutable source update with the same expiration time. - const lane = requestUpdateLane(fiber); - - markRootMutableRead(root, lane); - } catch (error) { - // A selector might throw after a source mutation. - // e.g. it might try to read from a part of the store that no longer exists. - // In this case we should still schedule an update with React. - // Worst case the selector will throw again and then an error boundary will handle it. - latestSetSnapshot( - (() => { - throw error; - }: any), - ); - } - }; - - const unsubscribe = subscribe(source._source, handleChange); - if (__DEV__) { - if (typeof unsubscribe !== 'function') { - console.error( - 'Mutable source subscribe function must return an unsubscribe function.', - ); - } - } - - return unsubscribe; - }, [source, subscribe]); - - // If any of the inputs to useMutableSource change, reading is potentially unsafe. - // - // If either the source or the subscription have changed we can't can't trust the update queue. - // Maybe the source changed in a way that the old subscription ignored but the new one depends on. - // - // If the getSnapshot function changed, we also shouldn't rely on the update queue. - // It's possible that the underlying source was mutated between the when the last "change" event fired, - // and when the current render (with the new getSnapshot function) is processed. - // - // In both cases, we need to throw away pending updates (since they are no longer relevant) - // and treat reading from the source as we do in the mount case. - if ( - !is(prevGetSnapshot, getSnapshot) || - !is(prevSource, source) || - !is(prevSubscribe, subscribe) - ) { - // Create a new queue and setState method, - // So if there are interleaved updates, they get pushed to the older queue. - // When this becomes current, the previous queue and dispatch method will be discarded, - // including any interleaving updates that occur. - const newQueue: UpdateQueue> = { - pending: null, - interleaved: null, - lanes: NoLanes, - dispatch: null, - lastRenderedReducer: basicStateReducer, - lastRenderedState: snapshot, - }; - newQueue.dispatch = setSnapshot = (dispatchAction.bind( - null, - currentlyRenderingFiber, - newQueue, - ): any); - stateHook.queue = newQueue; - stateHook.baseQueue = null; - snapshot = readFromUnsubscribedMutableSource(root, source, getSnapshot); - stateHook.memoizedState = stateHook.baseState = snapshot; - } - - return snapshot; -} - -function mountMutableSource( - source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn, - subscribe: MutableSourceSubscribeFn, -): Snapshot { - const hook = mountWorkInProgressHook(); - hook.memoizedState = ({ - refs: { - getSnapshot, - setSnapshot: (null: any), - }, - source, - subscribe, - }: MutableSourceMemoizedState); - return useMutableSource(hook, source, getSnapshot, subscribe); -} - -function updateMutableSource( - source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn, - subscribe: MutableSourceSubscribeFn, -): Snapshot { - const hook = updateWorkInProgressHook(); - return useMutableSource(hook, source, getSnapshot, subscribe); -} - function mountSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, @@ -2231,7 +1900,6 @@ export const ContextOnlyDispatcher: Dispatcher = { useDebugValue: throwInvalidHookError, useDeferredValue: throwInvalidHookError, useTransition: throwInvalidHookError, - useMutableSource: throwInvalidHookError, useSyncExternalStore: throwInvalidHookError, useOpaqueIdentifier: throwInvalidHookError, @@ -2257,7 +1925,6 @@ const HooksDispatcherOnMount: Dispatcher = { useDebugValue: mountDebugValue, useDeferredValue: mountDeferredValue, useTransition: mountTransition, - useMutableSource: mountMutableSource, useSyncExternalStore: mountSyncExternalStore, useOpaqueIdentifier: mountOpaqueIdentifier, @@ -2283,7 +1950,6 @@ const HooksDispatcherOnUpdate: Dispatcher = { useDebugValue: updateDebugValue, useDeferredValue: updateDeferredValue, useTransition: updateTransition, - useMutableSource: updateMutableSource, useSyncExternalStore: updateSyncExternalStore, useOpaqueIdentifier: updateOpaqueIdentifier, @@ -2309,7 +1975,6 @@ const HooksDispatcherOnRerender: Dispatcher = { useDebugValue: updateDebugValue, useDeferredValue: rerenderDeferredValue, useTransition: rerenderTransition, - useMutableSource: updateMutableSource, useSyncExternalStore: mountSyncExternalStore, useOpaqueIdentifier: rerenderOpaqueIdentifier, @@ -2450,15 +2115,6 @@ if (__DEV__) { mountHookTypesDev(); return mountTransition(); }, - useMutableSource( - source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn, - subscribe: MutableSourceSubscribeFn, - ): Snapshot { - currentHookNameInDev = 'useMutableSource'; - mountHookTypesDev(); - return mountMutableSource(source, getSnapshot, subscribe); - }, useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, @@ -2582,15 +2238,6 @@ if (__DEV__) { updateHookTypesDev(); return mountTransition(); }, - useMutableSource( - source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn, - subscribe: MutableSourceSubscribeFn, - ): Snapshot { - currentHookNameInDev = 'useMutableSource'; - updateHookTypesDev(); - return mountMutableSource(source, getSnapshot, subscribe); - }, useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, @@ -2714,15 +2361,6 @@ if (__DEV__) { updateHookTypesDev(); return updateTransition(); }, - useMutableSource( - source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn, - subscribe: MutableSourceSubscribeFn, - ): Snapshot { - currentHookNameInDev = 'useMutableSource'; - updateHookTypesDev(); - return updateMutableSource(source, getSnapshot, subscribe); - }, useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, @@ -2847,15 +2485,6 @@ if (__DEV__) { updateHookTypesDev(); return rerenderTransition(); }, - useMutableSource( - source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn, - subscribe: MutableSourceSubscribeFn, - ): Snapshot { - currentHookNameInDev = 'useMutableSource'; - updateHookTypesDev(); - return updateMutableSource(source, getSnapshot, subscribe); - }, useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, @@ -2992,16 +2621,6 @@ if (__DEV__) { mountHookTypesDev(); return mountTransition(); }, - useMutableSource( - source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn, - subscribe: MutableSourceSubscribeFn, - ): Snapshot { - currentHookNameInDev = 'useMutableSource'; - warnInvalidHookAccess(); - mountHookTypesDev(); - return mountMutableSource(source, getSnapshot, subscribe); - }, useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, @@ -3140,16 +2759,6 @@ if (__DEV__) { updateHookTypesDev(); return updateTransition(); }, - useMutableSource( - source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn, - subscribe: MutableSourceSubscribeFn, - ): Snapshot { - currentHookNameInDev = 'useMutableSource'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateMutableSource(source, getSnapshot, subscribe); - }, useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, @@ -3289,16 +2898,6 @@ if (__DEV__) { updateHookTypesDev(); return rerenderTransition(); }, - useMutableSource( - source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn, - subscribe: MutableSourceSubscribeFn, - ): Snapshot { - currentHookNameInDev = 'useMutableSource'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateMutableSource(source, getSnapshot, subscribe); - }, useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, diff --git a/packages/react-reconciler/src/ReactFiberLane.new.js b/packages/react-reconciler/src/ReactFiberLane.new.js index 2ad6b6a69a495..22840b4e806fe 100644 --- a/packages/react-reconciler/src/ReactFiberLane.new.js +++ b/packages/react-reconciler/src/ReactFiberLane.new.js @@ -283,9 +283,9 @@ export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes { // time it takes to show the final state, which is what they are actually // waiting for. // - // For those exceptions where entanglement is semantically important, like - // useMutableSource, we should ensure that there is no partial work at the - // time we apply the entanglement. + // For those exceptions where entanglement is semantically important, we + // should ensure that there is no partial work at the time we apply the + // entanglement. const entangledLanes = root.entangledLanes; if (entangledLanes !== NoLanes) { const entanglements = root.entanglements; @@ -618,10 +618,6 @@ export function markRootPinged( root.pingedLanes |= root.suspendedLanes & pingedLanes; } -export function markRootMutableRead(root: FiberRoot, updateLane: Lane) { - root.mutableReadLanes |= updateLane & root.pendingLanes; -} - export function markRootFinished(root: FiberRoot, remainingLanes: Lanes) { const noLongerPendingLanes = root.pendingLanes & ~remainingLanes; @@ -632,7 +628,6 @@ export function markRootFinished(root: FiberRoot, remainingLanes: Lanes) { root.pingedLanes = 0; root.expiredLanes &= remainingLanes; - root.mutableReadLanes &= remainingLanes; root.entangledLanes &= remainingLanes; diff --git a/packages/react-reconciler/src/ReactFiberLane.old.js b/packages/react-reconciler/src/ReactFiberLane.old.js index 3e704f54e6761..aae29502268af 100644 --- a/packages/react-reconciler/src/ReactFiberLane.old.js +++ b/packages/react-reconciler/src/ReactFiberLane.old.js @@ -283,9 +283,9 @@ export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes { // time it takes to show the final state, which is what they are actually // waiting for. // - // For those exceptions where entanglement is semantically important, like - // useMutableSource, we should ensure that there is no partial work at the - // time we apply the entanglement. + // For those exceptions where entanglement is semantically important, we + // should ensure that there is no partial work at the time we apply the + // entanglement. const entangledLanes = root.entangledLanes; if (entangledLanes !== NoLanes) { const entanglements = root.entanglements; @@ -618,10 +618,6 @@ export function markRootPinged( root.pingedLanes |= root.suspendedLanes & pingedLanes; } -export function markRootMutableRead(root: FiberRoot, updateLane: Lane) { - root.mutableReadLanes |= updateLane & root.pendingLanes; -} - export function markRootFinished(root: FiberRoot, remainingLanes: Lanes) { const noLongerPendingLanes = root.pendingLanes & ~remainingLanes; @@ -632,7 +628,6 @@ export function markRootFinished(root: FiberRoot, remainingLanes: Lanes) { root.pingedLanes = 0; root.expiredLanes &= remainingLanes; - root.mutableReadLanes &= remainingLanes; root.entangledLanes &= remainingLanes; diff --git a/packages/react-reconciler/src/ReactFiberReconciler.new.js b/packages/react-reconciler/src/ReactFiberReconciler.new.js index 06acfa11cbc3b..4219fdc72cf05 100644 --- a/packages/react-reconciler/src/ReactFiberReconciler.new.js +++ b/packages/react-reconciler/src/ReactFiberReconciler.new.js @@ -93,7 +93,6 @@ import { } from './ReactFiberHotReloading.new'; import {markRenderScheduled} from './SchedulingProfiler'; import ReactVersion from 'shared/ReactVersion'; -export {registerMutableSourceForHydration} from './ReactMutableSource.new'; export {createPortal} from './ReactPortal'; export { createComponentSelector, diff --git a/packages/react-reconciler/src/ReactFiberReconciler.old.js b/packages/react-reconciler/src/ReactFiberReconciler.old.js index 5a75a312c42b4..84bcb7e6e0d1a 100644 --- a/packages/react-reconciler/src/ReactFiberReconciler.old.js +++ b/packages/react-reconciler/src/ReactFiberReconciler.old.js @@ -93,7 +93,6 @@ import { } from './ReactFiberHotReloading.old'; import {markRenderScheduled} from './SchedulingProfiler'; import ReactVersion from 'shared/ReactVersion'; -export {registerMutableSourceForHydration} from './ReactMutableSource.old'; export {createPortal} from './ReactPortal'; export { createComponentSelector, diff --git a/packages/react-reconciler/src/ReactFiberRoot.new.js b/packages/react-reconciler/src/ReactFiberRoot.new.js index 5e1f8275b694f..d69d2d77e5dfa 100644 --- a/packages/react-reconciler/src/ReactFiberRoot.new.js +++ b/packages/react-reconciler/src/ReactFiberRoot.new.js @@ -49,7 +49,6 @@ function FiberRootNode(containerInfo, tag, hydrate) { this.suspendedLanes = NoLanes; this.pingedLanes = NoLanes; this.expiredLanes = NoLanes; - this.mutableReadLanes = NoLanes; this.finishedLanes = NoLanes; this.entangledLanes = NoLanes; diff --git a/packages/react-reconciler/src/ReactFiberRoot.old.js b/packages/react-reconciler/src/ReactFiberRoot.old.js index 413a87fbcffc8..8469d6c85b506 100644 --- a/packages/react-reconciler/src/ReactFiberRoot.old.js +++ b/packages/react-reconciler/src/ReactFiberRoot.old.js @@ -49,7 +49,6 @@ function FiberRootNode(containerInfo, tag, hydrate) { this.suspendedLanes = NoLanes; this.pingedLanes = NoLanes; this.expiredLanes = NoLanes; - this.mutableReadLanes = NoLanes; this.finishedLanes = NoLanes; this.entangledLanes = NoLanes; diff --git a/packages/react-reconciler/src/ReactFiberUnwindWork.new.js b/packages/react-reconciler/src/ReactFiberUnwindWork.new.js index 152837286f5d2..cb0d90e1a7f5f 100644 --- a/packages/react-reconciler/src/ReactFiberUnwindWork.new.js +++ b/packages/react-reconciler/src/ReactFiberUnwindWork.new.js @@ -13,7 +13,6 @@ import type {Lanes} from './ReactFiberLane.new'; import type {SuspenseState} from './ReactFiberSuspenseComponent.new'; import type {Cache, SpawnedCachePool} from './ReactFiberCacheComponent.new'; -import {resetWorkInProgressVersions as resetMutableSourceWorkInProgressVersions} from './ReactMutableSource.new'; import { ClassComponent, HostRoot, @@ -83,7 +82,6 @@ function unwindWork(workInProgress: Fiber, renderLanes: Lanes) { } popHostContainer(workInProgress); popTopLevelLegacyContextObject(workInProgress); - resetMutableSourceWorkInProgressVersions(); const flags = workInProgress.flags; invariant( (flags & DidCapture) === NoFlags, @@ -179,7 +177,6 @@ function unwindInterruptedWork(interruptedWork: Fiber, renderLanes: Lanes) { } popHostContainer(interruptedWork); popTopLevelLegacyContextObject(interruptedWork); - resetMutableSourceWorkInProgressVersions(); break; } case HostComponent: { diff --git a/packages/react-reconciler/src/ReactFiberUnwindWork.old.js b/packages/react-reconciler/src/ReactFiberUnwindWork.old.js index 88861db778be3..8fb63177c01ec 100644 --- a/packages/react-reconciler/src/ReactFiberUnwindWork.old.js +++ b/packages/react-reconciler/src/ReactFiberUnwindWork.old.js @@ -13,7 +13,6 @@ import type {Lanes} from './ReactFiberLane.old'; import type {SuspenseState} from './ReactFiberSuspenseComponent.old'; import type {Cache, SpawnedCachePool} from './ReactFiberCacheComponent.old'; -import {resetWorkInProgressVersions as resetMutableSourceWorkInProgressVersions} from './ReactMutableSource.old'; import { ClassComponent, HostRoot, @@ -83,7 +82,6 @@ function unwindWork(workInProgress: Fiber, renderLanes: Lanes) { } popHostContainer(workInProgress); popTopLevelLegacyContextObject(workInProgress); - resetMutableSourceWorkInProgressVersions(); const flags = workInProgress.flags; invariant( (flags & DidCapture) === NoFlags, @@ -179,7 +177,6 @@ function unwindInterruptedWork(interruptedWork: Fiber, renderLanes: Lanes) { } popHostContainer(interruptedWork); popTopLevelLegacyContextObject(interruptedWork); - resetMutableSourceWorkInProgressVersions(); break; } case HostComponent: { diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index 33bdb24af04db..4cde0bb44a70f 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -8,14 +8,7 @@ */ import type {Source} from 'shared/ReactElementType'; -import type { - RefObject, - ReactContext, - MutableSourceSubscribeFn, - MutableSourceGetSnapshotFn, - MutableSourceVersion, - MutableSource, -} from 'shared/ReactTypes'; +import type {RefObject, ReactContext} from 'shared/ReactTypes'; import type {SuspenseInstance} from './ReactFiberHostConfig'; import type {WorkTag} from './ReactWorkTags'; import type {TypeOfMode} from './ReactTypeOfMode'; @@ -40,7 +33,6 @@ export type HookType = | 'useDebugValue' | 'useDeferredValue' | 'useTransition' - | 'useMutableSource' | 'useSyncExternalStore' | 'useOpaqueIdentifier' | 'useCacheRefresh'; @@ -213,11 +205,6 @@ type BaseFiberRootProperties = {| // Determines if we should attempt to hydrate on the initial mount +hydrate: boolean, - // Used by useMutableSource hook to avoid tearing during hydration. - mutableSourceEagerHydrationData?: Array< - MutableSource | MutableSourceVersion, - > | null, - // Node returned by Scheduler.scheduleCallback. Represents the next rendering // task that the root will work on. callbackNode: *, @@ -229,7 +216,6 @@ type BaseFiberRootProperties = {| suspendedLanes: Lanes, pingedLanes: Lanes, expiredLanes: Lanes, - mutableReadLanes: Lanes, finishedLanes: Lanes, @@ -300,11 +286,6 @@ export type Dispatcher = {| useDebugValue(value: T, formatterFn: ?(value: T) => mixed): void, useDeferredValue(value: T): T, useTransition(): [boolean, (() => void) => void], - useMutableSource( - source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn, - subscribe: MutableSourceSubscribeFn, - ): Snapshot, useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, diff --git a/packages/react-reconciler/src/ReactMutableSource.new.js b/packages/react-reconciler/src/ReactMutableSource.new.js deleted file mode 100644 index 61809d33f800d..0000000000000 --- a/packages/react-reconciler/src/ReactMutableSource.new.js +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -import type {MutableSource, MutableSourceVersion} from 'shared/ReactTypes'; -import type {FiberRoot} from './ReactInternalTypes'; - -import {isPrimaryRenderer} from './ReactFiberHostConfig'; - -// Work in progress version numbers only apply to a single render, -// and should be reset before starting a new render. -// This tracks which mutable sources need to be reset after a render. -const workInProgressSources: Array> = []; - -let rendererSigil; -if (__DEV__) { - // Used to detect multiple renderers using the same mutable source. - rendererSigil = {}; -} - -export function markSourceAsDirty(mutableSource: MutableSource): void { - workInProgressSources.push(mutableSource); -} - -export function resetWorkInProgressVersions(): void { - for (let i = 0; i < workInProgressSources.length; i++) { - const mutableSource = workInProgressSources[i]; - if (isPrimaryRenderer) { - mutableSource._workInProgressVersionPrimary = null; - } else { - mutableSource._workInProgressVersionSecondary = null; - } - } - workInProgressSources.length = 0; -} - -export function getWorkInProgressVersion( - mutableSource: MutableSource, -): null | MutableSourceVersion { - if (isPrimaryRenderer) { - return mutableSource._workInProgressVersionPrimary; - } else { - return mutableSource._workInProgressVersionSecondary; - } -} - -export function setWorkInProgressVersion( - mutableSource: MutableSource, - version: MutableSourceVersion, -): void { - if (isPrimaryRenderer) { - mutableSource._workInProgressVersionPrimary = version; - } else { - mutableSource._workInProgressVersionSecondary = version; - } - workInProgressSources.push(mutableSource); -} - -export function warnAboutMultipleRenderersDEV( - mutableSource: MutableSource, -): void { - if (__DEV__) { - if (isPrimaryRenderer) { - if (mutableSource._currentPrimaryRenderer == null) { - mutableSource._currentPrimaryRenderer = rendererSigil; - } else if (mutableSource._currentPrimaryRenderer !== rendererSigil) { - console.error( - 'Detected multiple renderers concurrently rendering the ' + - 'same mutable source. This is currently unsupported.', - ); - } - } else { - if (mutableSource._currentSecondaryRenderer == null) { - mutableSource._currentSecondaryRenderer = rendererSigil; - } else if (mutableSource._currentSecondaryRenderer !== rendererSigil) { - console.error( - 'Detected multiple renderers concurrently rendering the ' + - 'same mutable source. This is currently unsupported.', - ); - } - } - } -} - -// Eager reads the version of a mutable source and stores it on the root. -// This ensures that the version used for server rendering matches the one -// that is eventually read during hydration. -// If they don't match there's a potential tear and a full deopt render is required. -export function registerMutableSourceForHydration( - root: FiberRoot, - mutableSource: MutableSource, -): void { - const getVersion = mutableSource._getVersion; - const version = getVersion(mutableSource._source); - - // TODO Clear this data once all pending hydration work is finished. - // Retaining it forever may interfere with GC. - if (root.mutableSourceEagerHydrationData == null) { - root.mutableSourceEagerHydrationData = [mutableSource, version]; - } else { - root.mutableSourceEagerHydrationData.push(mutableSource, version); - } -} diff --git a/packages/react-reconciler/src/ReactMutableSource.old.js b/packages/react-reconciler/src/ReactMutableSource.old.js deleted file mode 100644 index 61809d33f800d..0000000000000 --- a/packages/react-reconciler/src/ReactMutableSource.old.js +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -import type {MutableSource, MutableSourceVersion} from 'shared/ReactTypes'; -import type {FiberRoot} from './ReactInternalTypes'; - -import {isPrimaryRenderer} from './ReactFiberHostConfig'; - -// Work in progress version numbers only apply to a single render, -// and should be reset before starting a new render. -// This tracks which mutable sources need to be reset after a render. -const workInProgressSources: Array> = []; - -let rendererSigil; -if (__DEV__) { - // Used to detect multiple renderers using the same mutable source. - rendererSigil = {}; -} - -export function markSourceAsDirty(mutableSource: MutableSource): void { - workInProgressSources.push(mutableSource); -} - -export function resetWorkInProgressVersions(): void { - for (let i = 0; i < workInProgressSources.length; i++) { - const mutableSource = workInProgressSources[i]; - if (isPrimaryRenderer) { - mutableSource._workInProgressVersionPrimary = null; - } else { - mutableSource._workInProgressVersionSecondary = null; - } - } - workInProgressSources.length = 0; -} - -export function getWorkInProgressVersion( - mutableSource: MutableSource, -): null | MutableSourceVersion { - if (isPrimaryRenderer) { - return mutableSource._workInProgressVersionPrimary; - } else { - return mutableSource._workInProgressVersionSecondary; - } -} - -export function setWorkInProgressVersion( - mutableSource: MutableSource, - version: MutableSourceVersion, -): void { - if (isPrimaryRenderer) { - mutableSource._workInProgressVersionPrimary = version; - } else { - mutableSource._workInProgressVersionSecondary = version; - } - workInProgressSources.push(mutableSource); -} - -export function warnAboutMultipleRenderersDEV( - mutableSource: MutableSource, -): void { - if (__DEV__) { - if (isPrimaryRenderer) { - if (mutableSource._currentPrimaryRenderer == null) { - mutableSource._currentPrimaryRenderer = rendererSigil; - } else if (mutableSource._currentPrimaryRenderer !== rendererSigil) { - console.error( - 'Detected multiple renderers concurrently rendering the ' + - 'same mutable source. This is currently unsupported.', - ); - } - } else { - if (mutableSource._currentSecondaryRenderer == null) { - mutableSource._currentSecondaryRenderer = rendererSigil; - } else if (mutableSource._currentSecondaryRenderer !== rendererSigil) { - console.error( - 'Detected multiple renderers concurrently rendering the ' + - 'same mutable source. This is currently unsupported.', - ); - } - } - } -} - -// Eager reads the version of a mutable source and stores it on the root. -// This ensures that the version used for server rendering matches the one -// that is eventually read during hydration. -// If they don't match there's a potential tear and a full deopt render is required. -export function registerMutableSourceForHydration( - root: FiberRoot, - mutableSource: MutableSource, -): void { - const getVersion = mutableSource._getVersion; - const version = getVersion(mutableSource._source); - - // TODO Clear this data once all pending hydration work is finished. - // Retaining it forever may interfere with GC. - if (root.mutableSourceEagerHydrationData == null) { - root.mutableSourceEagerHydrationData = [mutableSource, version]; - } else { - root.mutableSourceEagerHydrationData.push(mutableSource, version); - } -} diff --git a/packages/react-server/src/ReactFizzHooks.js b/packages/react-server/src/ReactFizzHooks.js index 51ea84e8c43d1..bc2fc2bb6224b 100644 --- a/packages/react-server/src/ReactFizzHooks.js +++ b/packages/react-server/src/ReactFizzHooks.js @@ -9,12 +9,7 @@ import type {Dispatcher as DispatcherType} from 'react-reconciler/src/ReactInternalTypes'; -import type { - MutableSource, - MutableSourceGetSnapshotFn, - MutableSourceSubscribeFn, - ReactContext, -} from 'shared/ReactTypes'; +import type {ReactContext} from 'shared/ReactTypes'; import type {ResponseState, OpaqueIDType} from './ReactServerFormatConfig'; @@ -449,18 +444,6 @@ export function useCallback( return useMemo(() => callback, deps); } -// TODO Decide on how to implement this hook for server rendering. -// If a mutation occurs during render, consider triggering a Suspense boundary -// and falling back to client rendering. -function useMutableSource( - source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn, - subscribe: MutableSourceSubscribeFn, -): Snapshot { - resolveCurrentlyRenderingComponent(); - return getSnapshot(source._source); -} - function useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, @@ -514,8 +497,6 @@ export const Dispatcher: DispatcherType = { useDeferredValue, useTransition, useOpaqueIdentifier, - // Subscriptions are not setup in a server environment. - useMutableSource, useSyncExternalStore, }; diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index efac391833e8b..d7b6a6a08bcb7 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -826,7 +826,6 @@ const Dispatcher: DispatcherType = { useImperativeHandle: (unsupportedHook: any), useEffect: (unsupportedHook: any), useOpaqueIdentifier: (unsupportedHook: any), - useMutableSource: (unsupportedHook: any), useSyncExternalStore: (unsupportedHook: any), useCacheRefresh(): (?() => T, ?T) => void { return unsupportedRefresh; diff --git a/packages/react-suspense-test-utils/src/ReactSuspenseTestUtils.js b/packages/react-suspense-test-utils/src/ReactSuspenseTestUtils.js index ca898150b7cd2..1ed8755af5af9 100644 --- a/packages/react-suspense-test-utils/src/ReactSuspenseTestUtils.js +++ b/packages/react-suspense-test-utils/src/ReactSuspenseTestUtils.js @@ -43,7 +43,6 @@ export function waitForSuspense(fn: () => T): Promise { useDeferredValue: unsupported, useTransition: unsupported, useOpaqueIdentifier: unsupported, - useMutableSource: unsupported, useSyncExternalStore: unsupported, useCacheRefresh: unsupported, }; diff --git a/packages/react/index.classic.fb.js b/packages/react/index.classic.fb.js index 756f69f271336..1407290d5d177 100644 --- a/packages/react/index.classic.fb.js +++ b/packages/react/index.classic.fb.js @@ -49,8 +49,6 @@ export { useImperativeHandle, useLayoutEffect, useMemo, - useMutableSource, - useMutableSource as unstable_useMutableSource, useSyncExternalStore, useSyncExternalStore as unstable_useSyncExternalStore, useReducer, diff --git a/packages/react/index.experimental.js b/packages/react/index.experimental.js index 230e94f3a5280..9e6d942fa205a 100644 --- a/packages/react/index.experimental.js +++ b/packages/react/index.experimental.js @@ -44,7 +44,6 @@ export { useImperativeHandle, useLayoutEffect, useMemo, - useMutableSource as unstable_useMutableSource, useSyncExternalStore as unstable_useSyncExternalStore, useReducer, useRef, diff --git a/packages/react/index.js b/packages/react/index.js index c2285c6b0b312..aea59eb1f4efd 100644 --- a/packages/react/index.js +++ b/packages/react/index.js @@ -69,7 +69,6 @@ export { useImperativeHandle, useLayoutEffect, useMemo, - useMutableSource, useSyncExternalStore, useSyncExternalStore as unstable_useSyncExternalStore, useReducer, diff --git a/packages/react/index.modern.fb.js b/packages/react/index.modern.fb.js index d87d7c891ee88..e8889f2c81966 100644 --- a/packages/react/index.modern.fb.js +++ b/packages/react/index.modern.fb.js @@ -48,8 +48,6 @@ export { useImperativeHandle, useLayoutEffect, useMemo, - useMutableSource, - useMutableSource as unstable_useMutableSource, useSyncExternalStore, useSyncExternalStore as unstable_useSyncExternalStore, useReducer, diff --git a/packages/react/index.stable.js b/packages/react/index.stable.js index 008875e4577e5..da7875d1f947f 100644 --- a/packages/react/index.stable.js +++ b/packages/react/index.stable.js @@ -38,7 +38,6 @@ export { useImperativeHandle, useLayoutEffect, useMemo, - useMutableSource as unstable_useMutableSource, useReducer, useRef, useState, diff --git a/packages/react/src/React.js b/packages/react/src/React.js index 24421bd86c490..4573ffe10cd11 100644 --- a/packages/react/src/React.js +++ b/packages/react/src/React.js @@ -43,7 +43,6 @@ import { useDebugValue, useLayoutEffect, useMemo, - useMutableSource, useSyncExternalStore, useReducer, useRef, @@ -58,7 +57,6 @@ import { createFactoryWithValidation, cloneElementWithValidation, } from './ReactElementValidator'; -import {createMutableSource} from './ReactMutableSource'; import ReactSharedInternals from './ReactSharedInternals'; import {startTransition} from './ReactStartTransition'; import {act} from './ReactAct'; @@ -78,7 +76,6 @@ const Children = { export { Children, - createMutableSource, createRef, Component, PureComponent, @@ -93,7 +90,6 @@ export { useDebugValue, useLayoutEffect, useMemo, - useMutableSource, useSyncExternalStore, useReducer, useRef, diff --git a/packages/react/src/ReactHooks.js b/packages/react/src/ReactHooks.js index b284267b64e91..abdbd56e4c6c8 100644 --- a/packages/react/src/ReactHooks.js +++ b/packages/react/src/ReactHooks.js @@ -8,12 +8,7 @@ */ import type {Dispatcher} from 'react-reconciler/src/ReactInternalTypes'; -import type { - MutableSource, - MutableSourceGetSnapshotFn, - MutableSourceSubscribeFn, - ReactContext, -} from 'shared/ReactTypes'; +import type {ReactContext} from 'shared/ReactTypes'; import type {OpaqueIDType} from 'react-reconciler/src/ReactFiberHostConfig'; import ReactCurrentDispatcher from './ReactCurrentDispatcher'; @@ -160,15 +155,6 @@ export function useOpaqueIdentifier(): OpaqueIDType | void { return dispatcher.useOpaqueIdentifier(); } -export function useMutableSource( - source: MutableSource, - getSnapshot: MutableSourceGetSnapshotFn, - subscribe: MutableSourceSubscribeFn, -): Snapshot { - const dispatcher = resolveDispatcher(); - return dispatcher.useMutableSource(source, getSnapshot, subscribe); -} - export function useSyncExternalStore( subscribe: (() => void) => () => void, getSnapshot: () => T, diff --git a/packages/react/src/ReactMutableSource.js b/packages/react/src/ReactMutableSource.js deleted file mode 100644 index f8e5d0b283037..0000000000000 --- a/packages/react/src/ReactMutableSource.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -import type {MutableSource, MutableSourceGetVersionFn} from 'shared/ReactTypes'; - -export function createMutableSource>( - source: Source, - getVersion: MutableSourceGetVersionFn, -): MutableSource { - const mutableSource: MutableSource = { - _getVersion: getVersion, - _source: source, - _workInProgressVersionPrimary: null, - _workInProgressVersionSecondary: null, - }; - - if (__DEV__) { - mutableSource._currentPrimaryRenderer = null; - mutableSource._currentSecondaryRenderer = null; - - // Used to detect side effects that update a mutable source during render. - // See https://github.com/facebook/react/issues/19948 - mutableSource._currentlyRenderingFiber = null; - mutableSource._initialVersionAsOfFirstRender = null; - } - - return mutableSource; -} diff --git a/packages/react/unstable-shared-subset.experimental.js b/packages/react/unstable-shared-subset.experimental.js index 9381778b4435d..7e5a90ba327c0 100644 --- a/packages/react/unstable-shared-subset.experimental.js +++ b/packages/react/unstable-shared-subset.experimental.js @@ -33,7 +33,6 @@ export { useDeferredValue, useDeferredValue as unstable_useDeferredValue, useMemo, - useMutableSource as unstable_useMutableSource, useTransition, version, } from './src/React'; diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js index 43f42bddb91d3..277f633387d11 100644 --- a/packages/shared/ReactTypes.js +++ b/packages/shared/ReactTypes.js @@ -101,56 +101,6 @@ export type ReactScopeInstance = {| getChildContextValues: (context: ReactContext) => Array, |}; -// Mutable source version can be anything (e.g. number, string, immutable data structure) -// so long as it changes every time any part of the source changes. -export type MutableSourceVersion = $NonMaybeType; - -export type MutableSourceGetSnapshotFn< - Source: $NonMaybeType, - Snapshot, -> = (source: Source) => Snapshot; - -export type MutableSourceSubscribeFn, Snapshot> = ( - source: Source, - callback: (snapshot: Snapshot) => void, -) => () => void; - -export type MutableSourceGetVersionFn = ( - source: $NonMaybeType, -) => MutableSourceVersion; - -export type MutableSource> = {| - _source: Source, - - _getVersion: MutableSourceGetVersionFn, - - // Tracks the version of this source at the time it was most recently read. - // Used to determine if a source is safe to read from before it has been subscribed to. - // Version number is only used during mount, - // since the mechanism for determining safety after subscription is expiration time. - // - // As a workaround to support multiple concurrent renderers, - // we categorize some renderers as primary and others as secondary. - // We only expect there to be two concurrent renderers at most: - // React Native (primary) and Fabric (secondary); - // React DOM (primary) and React ART (secondary). - // Secondary renderers store their context values on separate fields. - // We use the same approach for Context. - _workInProgressVersionPrimary: null | MutableSourceVersion, - _workInProgressVersionSecondary: null | MutableSourceVersion, - - // DEV only - // Used to detect multiple renderers using the same mutable source. - _currentPrimaryRenderer?: Object | null, - _currentSecondaryRenderer?: Object | null, - - // DEV only - // Used to detect side effects that update a mutable source during render. - // See https://github.com/facebook/react/issues/19948 - _currentlyRenderingFiber?: Fiber | null, - _initialVersionAsOfFirstRender?: MutableSourceVersion | null, -|}; - // The subset of a Thenable required by things thrown by Suspense. // This doesn't require a value to be passed to either handler. export interface Wakeable {