Skip to content

Commit bd870a6

Browse files
author
Brian Vaughn
committed
useMutableSource hook
useMutableSource() enables React components to safely read from a mutable external source in Concurrent Mode. This API will detect mutations that occur during a render to avoid tearing. It will also automatically schedule updates when the source is mutated
1 parent d1bfdfb commit bd870a6

File tree

11 files changed

+736
-2
lines changed

11 files changed

+736
-2
lines changed

packages/react-debug-tools/src/ReactDebugHooks.js

+24
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ import type {
1313
ReactEventResponder,
1414
ReactEventResponderListener,
1515
} from 'shared/ReactTypes';
16+
import type {
17+
MutableSource,
18+
MutableSourceHookConfig,
19+
} from 'shared/ReactMutableSource';
1620
import type {Fiber} from 'react-reconciler/src/ReactFiber';
1721
import type {Hook, TimeoutConfig} from 'react-reconciler/src/ReactFiberHooks';
1822
import type {Dispatcher as DispatcherType} from 'react-reconciler/src/ReactFiberHooks';
@@ -67,6 +71,14 @@ function getPrimitiveStackCache(): Map<string, Array<any>> {
6771
Dispatcher.useDebugValue(null);
6872
Dispatcher.useCallback(() => {});
6973
Dispatcher.useMemo(() => null);
74+
Dispatcher.useMutableSource(
75+
{},
76+
{
77+
getVersion: () => null,
78+
getSnapshot: () => null,
79+
subscribe: () => () => {},
80+
},
81+
);
7082
} finally {
7183
readHookLog = hookLog;
7284
hookLog = [];
@@ -224,6 +236,17 @@ function useMemo<T>(
224236
return value;
225237
}
226238

239+
function useMutableSource<S>(
240+
source: MutableSource,
241+
config: MutableSourceHookConfig<S>,
242+
): S {
243+
const hook = nextHook();
244+
const getSnapshot = config.getSnapshot;
245+
const value = hook !== null ? hook.memoizedState.snapshot : getSnapshot();
246+
hookLog.push({primitive: 'MutableSource', stackError: new Error(), value});
247+
return value;
248+
}
249+
227250
function useResponder(
228251
responder: ReactEventResponder<any, any>,
229252
listenerProps: Object,
@@ -276,6 +299,7 @@ const Dispatcher: DispatcherType = {
276299
useState,
277300
useResponder,
278301
useTransition,
302+
useMutableSource,
279303
useDeferredValue,
280304
};
281305

packages/react-dom/src/server/ReactPartialRendererHooks.js

+15
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ import type {
1616
ReactContext,
1717
ReactEventResponderListener,
1818
} from 'shared/ReactTypes';
19+
import type {
20+
MutableSource,
21+
MutableSourceHookConfig,
22+
} from 'shared/ReactMutableSource';
1923
import type {SuspenseConfig} from 'react-reconciler/src/ReactFiberSuspenseConfig';
2024
import {validateContextBounds} from './ReactPartialRendererContext';
2125

@@ -459,6 +463,15 @@ function useResponder(responder, props): ReactEventResponderListener<any, any> {
459463
};
460464
}
461465

466+
function useMutableSource<S>(
467+
source: MutableSource,
468+
config: MutableSourceHookConfig<S>,
469+
): S {
470+
resolveCurrentlyRenderingComponent();
471+
const getSnapshot = config.getSnapshot;
472+
return getSnapshot();
473+
}
474+
462475
function useDeferredValue<T>(value: T, config: TimeoutConfig | null | void): T {
463476
resolveCurrentlyRenderingComponent();
464477
return value;
@@ -500,4 +513,6 @@ export const Dispatcher: DispatcherType = {
500513
useResponder,
501514
useDeferredValue,
502515
useTransition,
516+
// Subscriptions are not setup in a server environment.
517+
useMutableSource,
503518
};

0 commit comments

Comments
 (0)