@@ -19,6 +19,7 @@ import type {HookEffectTag} from './ReactHookEffectTags';
1919import type { SuspenseConfig } from './ReactFiberSuspenseConfig' ;
2020import type { ReactPriorityLevel } from './SchedulerWithReactIntegration' ;
2121
22+ import * as Scheduler from 'scheduler' ;
2223import ReactSharedInternals from 'shared/ReactSharedInternals' ;
2324
2425import { NoWork } from './ReactFiberExpirationTime' ;
@@ -54,7 +55,7 @@ import {markWorkInProgressReceivedUpdate} from './ReactFiberBeginWork';
5455import { requestCurrentSuspenseConfig } from './ReactFiberSuspenseConfig' ;
5556import { getCurrentPriorityLevel } from './SchedulerWithReactIntegration' ;
5657
57- const { ReactCurrentDispatcher} = ReactSharedInternals ;
58+ const { ReactCurrentDispatcher, ReactCurrentBatchConfig } = ReactSharedInternals ;
5859
5960export type Dispatcher = {
6061 readContext < T > (
@@ -92,6 +93,10 @@ export type Dispatcher = {
9293 responder : ReactEventResponder < E , C > ,
9394 props : Object ,
9495 ) : ReactEventResponderListener < E , C > ,
96+ useDeferredValue < T > ( value : T , config : TimeoutConfig | void | null ) : T ,
97+ useTransition (
98+ config : SuspenseConfig | void | null ,
99+ ) : [ ( ( ) => void ) => void , boolean ] ,
95100} ;
96101
97102type Update < S , A > = {
@@ -123,7 +128,9 @@ export type HookType =
123128 | 'useMemo'
124129 | 'useImperativeHandle'
125130 | 'useDebugValue'
126- | 'useResponder' ;
131+ | 'useResponder'
132+ | 'useDeferredValue'
133+ | 'useTransition' ;
127134
128135let didWarnAboutMismatchedHooksForComponent ;
129136if ( __DEV__ ) {
@@ -152,6 +159,10 @@ export type FunctionComponentUpdateQueue = {
152159 lastEffect : Effect | null ,
153160} ;
154161
162+ export type TimeoutConfig = { |
163+ timeoutMs : number ,
164+ | } ;
165+
155166type BasicStateAction < S > = ( S => S ) | S ;
156167
157168type Dispatch < A > = A => void ;
@@ -1117,6 +1128,96 @@ function updateMemo<T>(
11171128 return nextValue;
11181129}
11191130
1131+ function mountDeferredValue < T > (
1132+ value: T,
1133+ config: TimeoutConfig | void | null,
1134+ ): T {
1135+ const [ prevValue , setValue ] = mountState ( value ) ;
1136+ mountEffect (
1137+ ( ) => {
1138+ Scheduler . unstable_next ( ( ) => {
1139+ const previousConfig = ReactCurrentBatchConfig . suspense ;
1140+ ReactCurrentBatchConfig . suspense = config === undefined ? null : config ;
1141+ try {
1142+ setValue ( value ) ;
1143+ } finally {
1144+ ReactCurrentBatchConfig . suspense = previousConfig ;
1145+ }
1146+ } ) ;
1147+ } ,
1148+ [ value , config ] ,
1149+ ) ;
1150+ return prevValue ;
1151+ }
1152+
1153+ function updateDeferredValue< T > (
1154+ value: T,
1155+ config: TimeoutConfig | void | null,
1156+ ): T {
1157+ const [ prevValue , setValue ] = updateState ( value ) ;
1158+ updateEffect (
1159+ ( ) => {
1160+ Scheduler . unstable_next ( ( ) => {
1161+ const previousConfig = ReactCurrentBatchConfig . suspense ;
1162+ ReactCurrentBatchConfig . suspense = config === undefined ? null : config ;
1163+ try {
1164+ setValue ( value ) ;
1165+ } finally {
1166+ ReactCurrentBatchConfig . suspense = previousConfig ;
1167+ }
1168+ } ) ;
1169+ } ,
1170+ [ value , config ] ,
1171+ ) ;
1172+ return prevValue ;
1173+ }
1174+
1175+ function mountTransition(
1176+ config: SuspenseConfig | void | null,
1177+ ): [(() => void ) => void , boolean ] {
1178+ const [ isPending , setPending ] = mountState ( false ) ;
1179+ const startTransition = mountCallback (
1180+ callback => {
1181+ setPending ( true ) ;
1182+ Scheduler . unstable_next ( ( ) => {
1183+ const previousConfig = ReactCurrentBatchConfig . suspense ;
1184+ ReactCurrentBatchConfig . suspense = config === undefined ? null : config ;
1185+ try {
1186+ setPending ( false ) ;
1187+ callback ( ) ;
1188+ } finally {
1189+ ReactCurrentBatchConfig . suspense = previousConfig ;
1190+ }
1191+ } ) ;
1192+ } ,
1193+ [ config , isPending ] ,
1194+ ) ;
1195+ return [ startTransition , isPending ] ;
1196+ }
1197+
1198+ function updateTransition(
1199+ config: SuspenseConfig | void | null,
1200+ ): [(() => void ) => void , boolean ] {
1201+ const [ isPending , setPending ] = updateState ( false ) ;
1202+ const startTransition = updateCallback (
1203+ callback => {
1204+ setPending ( true ) ;
1205+ Scheduler . unstable_next ( ( ) => {
1206+ const previousConfig = ReactCurrentBatchConfig . suspense ;
1207+ ReactCurrentBatchConfig . suspense = config === undefined ? null : config ;
1208+ try {
1209+ setPending ( false ) ;
1210+ callback ( ) ;
1211+ } finally {
1212+ ReactCurrentBatchConfig . suspense = previousConfig ;
1213+ }
1214+ } ) ;
1215+ } ,
1216+ [ config , isPending ] ,
1217+ ) ;
1218+ return [ startTransition , isPending ] ;
1219+ }
1220+
11201221function dispatchAction< S , A > (
11211222 fiber: Fiber,
11221223 queue: UpdateQueue< S , A > ,
@@ -1272,6 +1373,8 @@ export const ContextOnlyDispatcher: Dispatcher = {
12721373 useState : throwInvalidHookError ,
12731374 useDebugValue : throwInvalidHookError ,
12741375 useResponder : throwInvalidHookError ,
1376+ useDeferredValue : throwInvalidHookError ,
1377+ useTransition : throwInvalidHookError ,
12751378} ;
12761379
12771380const HooksDispatcherOnMount: Dispatcher = {
@@ -1288,6 +1391,8 @@ const HooksDispatcherOnMount: Dispatcher = {
12881391 useState : mountState ,
12891392 useDebugValue : mountDebugValue ,
12901393 useResponder : createResponderListener ,
1394+ useDeferredValue : mountDeferredValue ,
1395+ useTransition : mountTransition ,
12911396} ;
12921397
12931398const HooksDispatcherOnUpdate: Dispatcher = {
@@ -1304,6 +1409,8 @@ const HooksDispatcherOnUpdate: Dispatcher = {
13041409 useState : updateState ,
13051410 useDebugValue : updateDebugValue ,
13061411 useResponder : createResponderListener ,
1412+ useDeferredValue : updateDeferredValue ,
1413+ useTransition : updateTransition ,
13071414} ;
13081415
13091416let HooksDispatcherOnMountInDEV: Dispatcher | null = null;
@@ -1441,6 +1548,18 @@ if (__DEV__) {
14411548 mountHookTypesDev ( ) ;
14421549 return createResponderListener ( responder , props ) ;
14431550 } ,
1551+ useDeferredValue< T > (value: T, config: TimeoutConfig | void | null): T {
1552+ currentHookNameInDev = 'useDeferredValue' ;
1553+ mountHookTypesDev ( ) ;
1554+ return mountDeferredValue ( value , config ) ;
1555+ } ,
1556+ useTransition(
1557+ config: SuspenseConfig | void | null,
1558+ ): [(() => void ) => void , boolean ] {
1559+ currentHookNameInDev = 'useTransition' ;
1560+ mountHookTypesDev ( ) ;
1561+ return mountTransition ( config ) ;
1562+ } ,
14441563 } ;
14451564
14461565 HooksDispatcherOnMountWithHookTypesInDEV = {
@@ -1546,6 +1665,18 @@ if (__DEV__) {
15461665 updateHookTypesDev ( ) ;
15471666 return createResponderListener ( responder , props ) ;
15481667 } ,
1668+ useDeferredValue< T > (value: T, config: TimeoutConfig | void | null): T {
1669+ currentHookNameInDev = 'useDeferredValue' ;
1670+ updateHookTypesDev ( ) ;
1671+ return mountDeferredValue ( value , config ) ;
1672+ } ,
1673+ useTransition(
1674+ config: SuspenseConfig | void | null,
1675+ ): [(() => void ) => void , boolean ] {
1676+ currentHookNameInDev = 'useTransition' ;
1677+ updateHookTypesDev ( ) ;
1678+ return mountTransition ( config ) ;
1679+ } ,
15491680 } ;
15501681
15511682 HooksDispatcherOnUpdateInDEV = {
@@ -1651,6 +1782,18 @@ if (__DEV__) {
16511782 updateHookTypesDev ( ) ;
16521783 return createResponderListener ( responder , props ) ;
16531784 } ,
1785+ useDeferredValue< T > (value: T, config: TimeoutConfig | void | null): T {
1786+ currentHookNameInDev = 'useDeferredValue' ;
1787+ updateHookTypesDev ( ) ;
1788+ return updateDeferredValue ( value , config ) ;
1789+ } ,
1790+ useTransition(
1791+ config: SuspenseConfig | void | null,
1792+ ): [(() => void ) => void , boolean ] {
1793+ currentHookNameInDev = 'useTransition' ;
1794+ updateHookTypesDev ( ) ;
1795+ return updateTransition ( config ) ;
1796+ } ,
16541797 } ;
16551798
16561799 InvalidNestedHooksDispatcherOnMountInDEV = {
@@ -1768,6 +1911,20 @@ if (__DEV__) {
17681911 mountHookTypesDev ( ) ;
17691912 return createResponderListener ( responder , props ) ;
17701913 } ,
1914+ useDeferredValue< T > (value: T, config: TimeoutConfig | void | null): T {
1915+ currentHookNameInDev = 'useDeferredValue' ;
1916+ warnInvalidHookAccess ( ) ;
1917+ mountHookTypesDev ( ) ;
1918+ return mountDeferredValue ( value , config ) ;
1919+ } ,
1920+ useTransition(
1921+ config: SuspenseConfig | void | null,
1922+ ): [(() => void ) => void , boolean ] {
1923+ currentHookNameInDev = 'useTransition' ;
1924+ warnInvalidHookAccess ( ) ;
1925+ mountHookTypesDev ( ) ;
1926+ return mountTransition ( config ) ;
1927+ } ,
17711928 } ;
17721929
17731930 InvalidNestedHooksDispatcherOnUpdateInDEV = {
@@ -1885,5 +2042,19 @@ if (__DEV__) {
18852042 updateHookTypesDev ( ) ;
18862043 return createResponderListener ( responder , props ) ;
18872044 } ,
2045+ useDeferredValue< T > (value: T, config: TimeoutConfig | void | null): T {
2046+ currentHookNameInDev = 'useDeferredValue' ;
2047+ warnInvalidHookAccess ( ) ;
2048+ updateHookTypesDev ( ) ;
2049+ return updateDeferredValue ( value , config ) ;
2050+ } ,
2051+ useTransition(
2052+ config: SuspenseConfig | void | null,
2053+ ): [(() => void ) => void , boolean ] {
2054+ currentHookNameInDev = 'useTransition' ;
2055+ warnInvalidHookAccess ( ) ;
2056+ updateHookTypesDev ( ) ;
2057+ return updateTransition ( config ) ;
2058+ } ,
18882059 } ;
18892060}
0 commit comments