@@ -19,6 +19,7 @@ import type {HookEffectTag} from './ReactHookEffectTags';
19
19
import type { SuspenseConfig } from './ReactFiberSuspenseConfig' ;
20
20
import type { ReactPriorityLevel } from './SchedulerWithReactIntegration' ;
21
21
22
+ import * as Scheduler from 'scheduler' ;
22
23
import ReactSharedInternals from 'shared/ReactSharedInternals' ;
23
24
24
25
import { NoWork } from './ReactFiberExpirationTime' ;
@@ -54,7 +55,7 @@ import {markWorkInProgressReceivedUpdate} from './ReactFiberBeginWork';
54
55
import { requestCurrentSuspenseConfig } from './ReactFiberSuspenseConfig' ;
55
56
import { getCurrentPriorityLevel } from './SchedulerWithReactIntegration' ;
56
57
57
- const { ReactCurrentDispatcher} = ReactSharedInternals ;
58
+ const { ReactCurrentDispatcher, ReactCurrentBatchConfig } = ReactSharedInternals ;
58
59
59
60
export type Dispatcher = {
60
61
readContext < T > (
@@ -92,6 +93,10 @@ export type Dispatcher = {
92
93
responder : ReactEventResponder < E , C > ,
93
94
props : Object ,
94
95
) : ReactEventResponderListener < E , C > ,
96
+ useDeferredValue < T > ( value : T , config : TimeoutConfig | void | null ) : T ,
97
+ useTransition (
98
+ config : SuspenseConfig | void | null ,
99
+ ) : [ ( ( ) => void ) => void , boolean ] ,
95
100
} ;
96
101
97
102
type Update < S , A > = {
@@ -123,7 +128,9 @@ export type HookType =
123
128
| 'useMemo'
124
129
| 'useImperativeHandle'
125
130
| 'useDebugValue'
126
- | 'useResponder' ;
131
+ | 'useResponder'
132
+ | 'useDeferredValue'
133
+ | 'useTransition' ;
127
134
128
135
let didWarnAboutMismatchedHooksForComponent ;
129
136
if ( __DEV__ ) {
@@ -152,6 +159,10 @@ export type FunctionComponentUpdateQueue = {
152
159
lastEffect : Effect | null ,
153
160
} ;
154
161
162
+ export type TimeoutConfig = { |
163
+ timeoutMs : number ,
164
+ | } ;
165
+
155
166
type BasicStateAction < S > = ( S => S ) | S ;
156
167
157
168
type Dispatch < A > = A => void ;
@@ -1117,6 +1128,96 @@ function updateMemo<T>(
1117
1128
return nextValue;
1118
1129
}
1119
1130
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
+
1120
1221
function dispatchAction< S , A > (
1121
1222
fiber: Fiber,
1122
1223
queue: UpdateQueue< S , A > ,
@@ -1272,6 +1373,8 @@ export const ContextOnlyDispatcher: Dispatcher = {
1272
1373
useState : throwInvalidHookError ,
1273
1374
useDebugValue : throwInvalidHookError ,
1274
1375
useResponder : throwInvalidHookError ,
1376
+ useDeferredValue : throwInvalidHookError ,
1377
+ useTransition : throwInvalidHookError ,
1275
1378
} ;
1276
1379
1277
1380
const HooksDispatcherOnMount: Dispatcher = {
@@ -1288,6 +1391,8 @@ const HooksDispatcherOnMount: Dispatcher = {
1288
1391
useState : mountState ,
1289
1392
useDebugValue : mountDebugValue ,
1290
1393
useResponder : createResponderListener ,
1394
+ useDeferredValue : mountDeferredValue ,
1395
+ useTransition : mountTransition ,
1291
1396
} ;
1292
1397
1293
1398
const HooksDispatcherOnUpdate: Dispatcher = {
@@ -1304,6 +1409,8 @@ const HooksDispatcherOnUpdate: Dispatcher = {
1304
1409
useState : updateState ,
1305
1410
useDebugValue : updateDebugValue ,
1306
1411
useResponder : createResponderListener ,
1412
+ useDeferredValue : updateDeferredValue ,
1413
+ useTransition : updateTransition ,
1307
1414
} ;
1308
1415
1309
1416
let HooksDispatcherOnMountInDEV: Dispatcher | null = null;
@@ -1441,6 +1548,18 @@ if (__DEV__) {
1441
1548
mountHookTypesDev ( ) ;
1442
1549
return createResponderListener ( responder , props ) ;
1443
1550
} ,
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
+ } ,
1444
1563
} ;
1445
1564
1446
1565
HooksDispatcherOnMountWithHookTypesInDEV = {
@@ -1546,6 +1665,18 @@ if (__DEV__) {
1546
1665
updateHookTypesDev ( ) ;
1547
1666
return createResponderListener ( responder , props ) ;
1548
1667
} ,
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
+ } ,
1549
1680
} ;
1550
1681
1551
1682
HooksDispatcherOnUpdateInDEV = {
@@ -1651,6 +1782,18 @@ if (__DEV__) {
1651
1782
updateHookTypesDev ( ) ;
1652
1783
return createResponderListener ( responder , props ) ;
1653
1784
} ,
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
+ } ,
1654
1797
} ;
1655
1798
1656
1799
InvalidNestedHooksDispatcherOnMountInDEV = {
@@ -1768,6 +1911,20 @@ if (__DEV__) {
1768
1911
mountHookTypesDev ( ) ;
1769
1912
return createResponderListener ( responder , props ) ;
1770
1913
} ,
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
+ } ,
1771
1928
} ;
1772
1929
1773
1930
InvalidNestedHooksDispatcherOnUpdateInDEV = {
@@ -1885,5 +2042,19 @@ if (__DEV__) {
1885
2042
updateHookTypesDev ( ) ;
1886
2043
return createResponderListener ( responder , props ) ;
1887
2044
} ,
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
+ } ,
1888
2059
} ;
1889
2060
}
0 commit comments