Skip to content

Commit

Permalink
Migrate useDeferredValue and useTransition (#17058)
Browse files Browse the repository at this point in the history
Migrated useDeferredValue and useTransition from Facebook's www repo into ReactFiberHooks.
  • Loading branch information
lunaruan authored Oct 18, 2019
1 parent 0b61e26 commit 685ed56
Show file tree
Hide file tree
Showing 9 changed files with 544 additions and 19 deletions.
27 changes: 26 additions & 1 deletion packages/react-debug-tools/src/ReactDebugHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ import type {
ReactEventResponderListener,
} from 'shared/ReactTypes';
import type {Fiber} from 'react-reconciler/src/ReactFiber';
import type {Hook} from 'react-reconciler/src/ReactFiberHooks';
import type {Hook, TimeoutConfig} from 'react-reconciler/src/ReactFiberHooks';
import type {Dispatcher as DispatcherType} from 'react-reconciler/src/ReactFiberHooks';
import type {SuspenseConfig} from 'react-reconciler/src/ReactFiberSuspenseConfig';

import ErrorStackParser from 'error-stack-parser';
import ReactSharedInternals from 'shared/ReactSharedInternals';
Expand Down Expand Up @@ -236,6 +237,28 @@ function useResponder(
};
}

function useTransition(
config: SuspenseConfig | null | void,
): [(() => void) => void, boolean] {
nextHook();
hookLog.push({
primitive: 'Transition',
stackError: new Error(),
value: config,
});
return [callback => {}, false];
}

function useDeferredValue<T>(value: T, config: TimeoutConfig | null | void): T {
nextHook();
hookLog.push({
primitive: 'DeferredValue',
stackError: new Error(),
value,
});
return value;
}

const Dispatcher: DispatcherType = {
readContext,
useCallback,
Expand All @@ -249,6 +272,8 @@ const Dispatcher: DispatcherType = {
useRef,
useState,
useResponder,
useTransition,
useDeferredValue,
};

// Inspect
Expand Down
24 changes: 22 additions & 2 deletions packages/react-dom/src/server/ReactPartialRendererHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@
* @flow
*/

import type {Dispatcher as DispatcherType} from 'react-reconciler/src/ReactFiberHooks';
import type {
Dispatcher as DispatcherType,
TimeoutConfig,
} from 'react-reconciler/src/ReactFiberHooks';
import type {ThreadID} from './ReactThreadIDAllocator';
import type {
ReactContext,
ReactEventResponderListener,
} from 'shared/ReactTypes';

import type {SuspenseConfig} from 'react-reconciler/src/ReactFiberSuspenseConfig';
import {validateContextBounds} from './ReactPartialRendererContext';

import invariant from 'shared/invariant';
Expand Down Expand Up @@ -457,6 +460,21 @@ function useResponder(responder, props): ReactEventResponderListener<any, any> {
};
}

function useDeferredValue<T>(value: T, config: TimeoutConfig | null | void): T {
resolveCurrentlyRenderingComponent();
return value;
}

function useTransition(
config: SuspenseConfig | null | void,
): [(callback: () => void) => void, boolean] {
resolveCurrentlyRenderingComponent();
const startTransition = callback => {
callback();
};
return [startTransition, false];
}

function noop(): void {}

export let currentThreadID: ThreadID = 0;
Expand All @@ -481,4 +499,6 @@ export const Dispatcher: DispatcherType = {
// Debugging effect
useDebugValue: noop,
useResponder,
useDeferredValue,
useTransition,
};
175 changes: 173 additions & 2 deletions packages/react-reconciler/src/ReactFiberHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import type {HookEffectTag} from './ReactHookEffectTags';
import type {SuspenseConfig} from './ReactFiberSuspenseConfig';
import type {ReactPriorityLevel} from './SchedulerWithReactIntegration';

import * as Scheduler from 'scheduler';
import ReactSharedInternals from 'shared/ReactSharedInternals';

import {NoWork} from './ReactFiberExpirationTime';
Expand Down Expand Up @@ -54,7 +55,7 @@ import {markWorkInProgressReceivedUpdate} from './ReactFiberBeginWork';
import {requestCurrentSuspenseConfig} from './ReactFiberSuspenseConfig';
import {getCurrentPriorityLevel} from './SchedulerWithReactIntegration';

const {ReactCurrentDispatcher} = ReactSharedInternals;
const {ReactCurrentDispatcher, ReactCurrentBatchConfig} = ReactSharedInternals;

export type Dispatcher = {
readContext<T>(
Expand Down Expand Up @@ -92,6 +93,10 @@ export type Dispatcher = {
responder: ReactEventResponder<E, C>,
props: Object,
): ReactEventResponderListener<E, C>,
useDeferredValue<T>(value: T, config: TimeoutConfig | void | null): T,
useTransition(
config: SuspenseConfig | void | null,
): [(() => void) => void, boolean],
};

type Update<S, A> = {
Expand Down Expand Up @@ -123,7 +128,9 @@ export type HookType =
| 'useMemo'
| 'useImperativeHandle'
| 'useDebugValue'
| 'useResponder';
| 'useResponder'
| 'useDeferredValue'
| 'useTransition';

let didWarnAboutMismatchedHooksForComponent;
if (__DEV__) {
Expand Down Expand Up @@ -152,6 +159,10 @@ export type FunctionComponentUpdateQueue = {
lastEffect: Effect | null,
};

export type TimeoutConfig = {|
timeoutMs: number,
|};

type BasicStateAction<S> = (S => S) | S;

type Dispatch<A> = A => void;
Expand Down Expand Up @@ -1117,6 +1128,96 @@ function updateMemo<T>(
return nextValue;
}

function mountDeferredValue<T>(
value: T,
config: TimeoutConfig | void | null,
): T {
const [prevValue, setValue] = mountState(value);
mountEffect(
() => {
Scheduler.unstable_next(() => {
const previousConfig = ReactCurrentBatchConfig.suspense;
ReactCurrentBatchConfig.suspense = config === undefined ? null : config;
try {
setValue(value);
} finally {
ReactCurrentBatchConfig.suspense = previousConfig;
}
});
},
[value, config],
);
return prevValue;
}

function updateDeferredValue<T>(
value: T,
config: TimeoutConfig | void | null,
): T {
const [prevValue, setValue] = updateState(value);
updateEffect(
() => {
Scheduler.unstable_next(() => {
const previousConfig = ReactCurrentBatchConfig.suspense;
ReactCurrentBatchConfig.suspense = config === undefined ? null : config;
try {
setValue(value);
} finally {
ReactCurrentBatchConfig.suspense = previousConfig;
}
});
},
[value, config],
);
return prevValue;
}

function mountTransition(
config: SuspenseConfig | void | null,
): [(() => void) => void, boolean] {
const [isPending, setPending] = mountState(false);
const startTransition = mountCallback(
callback => {
setPending(true);
Scheduler.unstable_next(() => {
const previousConfig = ReactCurrentBatchConfig.suspense;
ReactCurrentBatchConfig.suspense = config === undefined ? null : config;
try {
setPending(false);
callback();
} finally {
ReactCurrentBatchConfig.suspense = previousConfig;
}
});
},
[config, isPending],
);
return [startTransition, isPending];
}

function updateTransition(
config: SuspenseConfig | void | null,
): [(() => void) => void, boolean] {
const [isPending, setPending] = updateState(false);
const startTransition = updateCallback(
callback => {
setPending(true);
Scheduler.unstable_next(() => {
const previousConfig = ReactCurrentBatchConfig.suspense;
ReactCurrentBatchConfig.suspense = config === undefined ? null : config;
try {
setPending(false);
callback();
} finally {
ReactCurrentBatchConfig.suspense = previousConfig;
}
});
},
[config, isPending],
);
return [startTransition, isPending];
}

function dispatchAction<S, A>(
fiber: Fiber,
queue: UpdateQueue<S, A>,
Expand Down Expand Up @@ -1272,6 +1373,8 @@ export const ContextOnlyDispatcher: Dispatcher = {
useState: throwInvalidHookError,
useDebugValue: throwInvalidHookError,
useResponder: throwInvalidHookError,
useDeferredValue: throwInvalidHookError,
useTransition: throwInvalidHookError,
};

const HooksDispatcherOnMount: Dispatcher = {
Expand All @@ -1288,6 +1391,8 @@ const HooksDispatcherOnMount: Dispatcher = {
useState: mountState,
useDebugValue: mountDebugValue,
useResponder: createResponderListener,
useDeferredValue: mountDeferredValue,
useTransition: mountTransition,
};

const HooksDispatcherOnUpdate: Dispatcher = {
Expand All @@ -1304,6 +1409,8 @@ const HooksDispatcherOnUpdate: Dispatcher = {
useState: updateState,
useDebugValue: updateDebugValue,
useResponder: createResponderListener,
useDeferredValue: updateDeferredValue,
useTransition: updateTransition,
};

let HooksDispatcherOnMountInDEV: Dispatcher | null = null;
Expand Down Expand Up @@ -1441,6 +1548,18 @@ if (__DEV__) {
mountHookTypesDev();
return createResponderListener(responder, props);
},
useDeferredValue<T>(value: T, config: TimeoutConfig | void | null): T {
currentHookNameInDev = 'useDeferredValue';
mountHookTypesDev();
return mountDeferredValue(value, config);
},
useTransition(
config: SuspenseConfig | void | null,
): [(() => void) => void, boolean] {
currentHookNameInDev = 'useTransition';
mountHookTypesDev();
return mountTransition(config);
},
};

HooksDispatcherOnMountWithHookTypesInDEV = {
Expand Down Expand Up @@ -1546,6 +1665,18 @@ if (__DEV__) {
updateHookTypesDev();
return createResponderListener(responder, props);
},
useDeferredValue<T>(value: T, config: TimeoutConfig | void | null): T {
currentHookNameInDev = 'useDeferredValue';
updateHookTypesDev();
return mountDeferredValue(value, config);
},
useTransition(
config: SuspenseConfig | void | null,
): [(() => void) => void, boolean] {
currentHookNameInDev = 'useTransition';
updateHookTypesDev();
return mountTransition(config);
},
};

HooksDispatcherOnUpdateInDEV = {
Expand Down Expand Up @@ -1651,6 +1782,18 @@ if (__DEV__) {
updateHookTypesDev();
return createResponderListener(responder, props);
},
useDeferredValue<T>(value: T, config: TimeoutConfig | void | null): T {
currentHookNameInDev = 'useDeferredValue';
updateHookTypesDev();
return updateDeferredValue(value, config);
},
useTransition(
config: SuspenseConfig | void | null,
): [(() => void) => void, boolean] {
currentHookNameInDev = 'useTransition';
updateHookTypesDev();
return updateTransition(config);
},
};

InvalidNestedHooksDispatcherOnMountInDEV = {
Expand Down Expand Up @@ -1768,6 +1911,20 @@ if (__DEV__) {
mountHookTypesDev();
return createResponderListener(responder, props);
},
useDeferredValue<T>(value: T, config: TimeoutConfig | void | null): T {
currentHookNameInDev = 'useDeferredValue';
warnInvalidHookAccess();
mountHookTypesDev();
return mountDeferredValue(value, config);
},
useTransition(
config: SuspenseConfig | void | null,
): [(() => void) => void, boolean] {
currentHookNameInDev = 'useTransition';
warnInvalidHookAccess();
mountHookTypesDev();
return mountTransition(config);
},
};

InvalidNestedHooksDispatcherOnUpdateInDEV = {
Expand Down Expand Up @@ -1885,5 +2042,19 @@ if (__DEV__) {
updateHookTypesDev();
return createResponderListener(responder, props);
},
useDeferredValue<T>(value: T, config: TimeoutConfig | void | null): T {
currentHookNameInDev = 'useDeferredValue';
warnInvalidHookAccess();
updateHookTypesDev();
return updateDeferredValue(value, config);
},
useTransition(
config: SuspenseConfig | void | null,
): [(() => void) => void, boolean] {
currentHookNameInDev = 'useTransition';
warnInvalidHookAccess();
updateHookTypesDev();
return updateTransition(config);
},
};
}
2 changes: 1 addition & 1 deletion packages/react-reconciler/src/ReactFiberWorkLoop.js
Original file line number Diff line number Diff line change
Expand Up @@ -2930,7 +2930,7 @@ function flushSuspensePriorityWarningInDEV() {
'update to provide immediate feedback, and another update that ' +
'triggers the bulk of the changes.' +
'\n\n' +
'Refer to the documentation for useSuspenseTransition to learn how ' +
'Refer to the documentation for useTransition to learn how ' +
'to implement this pattern.',
// TODO: Add link to React docs with more information, once it exists
componentNames.sort().join(', '),
Expand Down
Loading

0 comments on commit 685ed56

Please sign in to comment.