@@ -63,21 +63,6 @@ describe('ReactDOMForm', () => {
63
63
textCache = new Map ( ) ;
64
64
} ) ;
65
65
66
- function resolveText ( text ) {
67
- const record = textCache . get ( text ) ;
68
- if ( record === undefined ) {
69
- const newRecord = {
70
- status : 'resolved' ,
71
- value : text ,
72
- } ;
73
- textCache . set ( text , newRecord ) ;
74
- } else if ( record . status === 'pending' ) {
75
- const thenable = record . value ;
76
- record . status = 'resolved' ;
77
- record . value = text ;
78
- thenable . pings . forEach ( t => t ( ) ) ;
79
- }
80
- }
81
66
function resolveText ( text ) {
82
67
const record = textCache . get ( text ) ;
83
68
if ( record === undefined ) {
@@ -997,19 +982,20 @@ describe('ReactDOMForm', () => {
997
982
998
983
let dispatch ;
999
984
function App ( ) {
1000
- const [ state , _dispatch ] = useFormState ( action , 0 ) ;
985
+ const [ state , _dispatch , isPending ] = useFormState ( action , 0 ) ;
1001
986
dispatch = _dispatch ;
1002
- return < Text text = { state } /> ;
987
+ const pending = isPending ? 'Pending ' : '' ;
988
+ return < Text text = { pending + state } /> ;
1003
989
}
1004
990
1005
991
const root = ReactDOMClient . createRoot ( container ) ;
1006
992
await act ( ( ) => root . render ( < App /> ) ) ;
1007
- assertLog ( [ 0 ] ) ;
993
+ assertLog ( [ '0' ] ) ;
1008
994
expect ( container . textContent ) . toBe ( '0' ) ;
1009
995
1010
996
await act ( ( ) => dispatch ( 'increment' ) ) ;
1011
- assertLog ( [ 'Async action started [1]' ] ) ;
1012
- expect ( container . textContent ) . toBe ( '0' ) ;
997
+ assertLog ( [ 'Async action started [1]' , 'Pending 0' ] ) ;
998
+ expect ( container . textContent ) . toBe ( 'Pending 0' ) ;
1013
999
1014
1000
// Dispatch a few more actions. None of these will start until the previous
1015
1001
// one finishes.
@@ -1031,7 +1017,7 @@ describe('ReactDOMForm', () => {
1031
1017
await act ( ( ) => resolveText ( 'Wait [4]' ) ) ;
1032
1018
1033
1019
// Finally the last action finishes and we can render the result.
1034
- assertLog ( [ 2 ] ) ;
1020
+ assertLog ( [ '2' ] ) ;
1035
1021
expect ( container . textContent ) . toBe ( '2' ) ;
1036
1022
} ) ;
1037
1023
@@ -1040,40 +1026,42 @@ describe('ReactDOMForm', () => {
1040
1026
test ( 'useFormState supports inline actions' , async ( ) => {
1041
1027
let increment ;
1042
1028
function App ( { stepSize} ) {
1043
- const [ state , dispatch ] = useFormState ( async prevState => {
1029
+ const [ state , dispatch , isPending ] = useFormState ( async prevState => {
1044
1030
return prevState + stepSize ;
1045
1031
} , 0 ) ;
1046
1032
increment = dispatch ;
1047
- return < Text text = { state } /> ;
1033
+ const pending = isPending ? 'Pending ' : '' ;
1034
+ return < Text text = { pending + state } /> ;
1048
1035
}
1049
1036
1050
1037
// Initial render
1051
1038
const root = ReactDOMClient . createRoot ( container ) ;
1052
1039
await act ( ( ) => root . render ( < App stepSize = { 1 } /> ) ) ;
1053
- assertLog ( [ 0 ] ) ;
1040
+ assertLog ( [ '0' ] ) ;
1054
1041
1055
1042
// Perform an action. This will increase the state by 1, as defined by the
1056
1043
// stepSize prop.
1057
1044
await act ( ( ) => increment ( ) ) ;
1058
- assertLog ( [ 1 ] ) ;
1045
+ assertLog ( [ 'Pending 0' , '1' ] ) ;
1059
1046
1060
1047
// Now increase the stepSize prop to 10. Subsequent steps will increase
1061
1048
// by this amount.
1062
1049
await act ( ( ) => root . render ( < App stepSize = { 10 } /> ) ) ;
1063
- assertLog ( [ 1 ] ) ;
1050
+ assertLog ( [ '1' ] ) ;
1064
1051
1065
1052
// Increment again. The state should increase by 10.
1066
1053
await act ( ( ) => increment ( ) ) ;
1067
- assertLog ( [ 11 ] ) ;
1054
+ assertLog ( [ 'Pending 1' , '11' ] ) ;
1068
1055
} ) ;
1069
1056
1070
1057
// @gate enableFormActions
1071
1058
// @gate enableAsyncActions
1072
1059
test ( 'useFormState: dispatch throws if called during render' , async ( ) => {
1073
1060
function App ( ) {
1074
- const [ state , dispatch ] = useFormState ( async ( ) => { } , 0 ) ;
1061
+ const [ state , dispatch , isPending ] = useFormState ( async ( ) => { } , 0 ) ;
1075
1062
dispatch ( ) ;
1076
- return < Text text = { state } /> ;
1063
+ const pending = isPending ? 'Pending ' : '' ;
1064
+ return < Text text = { pending + state } /> ;
1077
1065
}
1078
1066
1079
1067
const root = ReactDOMClient . createRoot ( container ) ;
@@ -1088,21 +1076,25 @@ describe('ReactDOMForm', () => {
1088
1076
test ( 'queues multiple actions and runs them in order' , async ( ) => {
1089
1077
let action ;
1090
1078
function App ( ) {
1091
- const [ state , dispatch ] = useFormState (
1079
+ const [ state , dispatch , isPending ] = useFormState (
1092
1080
async ( s , a ) => await getText ( a ) ,
1093
1081
'A' ,
1094
1082
) ;
1095
1083
action = dispatch ;
1096
- return < Text text = { state } /> ;
1084
+ const pending = isPending ? 'Pending ' : '' ;
1085
+ return < Text text = { pending + state } /> ;
1097
1086
}
1098
1087
1099
1088
const root = ReactDOMClient . createRoot ( container ) ;
1100
1089
await act ( ( ) => root . render ( < App /> ) ) ;
1101
1090
assertLog ( [ 'A' ] ) ;
1102
1091
1103
1092
await act ( ( ) => action ( 'B' ) ) ;
1093
+ // The first dispatch will update the pending state.
1094
+ assertLog ( [ 'Pending A' ] ) ;
1104
1095
await act ( ( ) => action ( 'C' ) ) ;
1105
1096
await act ( ( ) => action ( 'D' ) ) ;
1097
+ assertLog ( [ ] ) ;
1106
1098
1107
1099
await act ( ( ) => resolveText ( 'B' ) ) ;
1108
1100
await act ( ( ) => resolveText ( 'C' ) ) ;
@@ -1117,51 +1109,56 @@ describe('ReactDOMForm', () => {
1117
1109
test ( 'useFormState: works if action is sync' , async ( ) => {
1118
1110
let increment ;
1119
1111
function App ( { stepSize} ) {
1120
- const [ state , dispatch ] = useFormState ( prevState => {
1112
+ const [ state , dispatch , isPending ] = useFormState ( prevState => {
1121
1113
return prevState + stepSize ;
1122
1114
} , 0 ) ;
1123
1115
increment = dispatch ;
1124
- return < Text text = { state } /> ;
1116
+ const pending = isPending ? 'Pending ' : '' ;
1117
+ return < Text text = { pending + state } /> ;
1125
1118
}
1126
1119
1127
1120
// Initial render
1128
1121
const root = ReactDOMClient . createRoot ( container ) ;
1129
1122
await act ( ( ) => root . render ( < App stepSize = { 1 } /> ) ) ;
1130
- assertLog ( [ 0 ] ) ;
1123
+ assertLog ( [ '0' ] ) ;
1131
1124
1132
1125
// Perform an action. This will increase the state by 1, as defined by the
1133
1126
// stepSize prop.
1134
1127
await act ( ( ) => increment ( ) ) ;
1135
- assertLog ( [ 1 ] ) ;
1128
+ assertLog ( [ 'Pending 0' , '1' ] ) ;
1136
1129
1137
1130
// Now increase the stepSize prop to 10. Subsequent steps will increase
1138
1131
// by this amount.
1139
1132
await act ( ( ) => root . render ( < App stepSize = { 10 } /> ) ) ;
1140
- assertLog ( [ 1 ] ) ;
1133
+ assertLog ( [ '1' ] ) ;
1141
1134
1142
1135
// Increment again. The state should increase by 10.
1143
1136
await act ( ( ) => increment ( ) ) ;
1144
- assertLog ( [ 11 ] ) ;
1137
+ assertLog ( [ 'Pending 1' , '11' ] ) ;
1145
1138
} ) ;
1146
1139
1147
1140
// @gate enableFormActions
1148
1141
// @gate enableAsyncActions
1149
1142
test ( 'useFormState: can mix sync and async actions' , async ( ) => {
1150
1143
let action ;
1151
1144
function App ( ) {
1152
- const [ state , dispatch ] = useFormState ( ( s , a ) => a , 'A' ) ;
1145
+ const [ state , dispatch , isPending ] = useFormState ( ( s , a ) => a , 'A' ) ;
1153
1146
action = dispatch ;
1154
- return < Text text = { state } /> ;
1147
+ const pending = isPending ? 'Pending ' : '' ;
1148
+ return < Text text = { pending + state } /> ;
1155
1149
}
1156
1150
1157
1151
const root = ReactDOMClient . createRoot ( container ) ;
1158
1152
await act ( ( ) => root . render ( < App /> ) ) ;
1159
1153
assertLog ( [ 'A' ] ) ;
1160
1154
1161
1155
await act ( ( ) => action ( getText ( 'B' ) ) ) ;
1156
+ // The first dispatch will update the pending state.
1157
+ assertLog ( [ 'Pending A' ] ) ;
1162
1158
await act ( ( ) => action ( 'C' ) ) ;
1163
1159
await act ( ( ) => action ( getText ( 'D' ) ) ) ;
1164
1160
await act ( ( ) => action ( 'E' ) ) ;
1161
+ assertLog ( [ ] ) ;
1165
1162
1166
1163
await act ( ( ) => resolveText ( 'B' ) ) ;
1167
1164
await act ( ( ) => resolveText ( 'D' ) ) ;
@@ -1189,14 +1186,15 @@ describe('ReactDOMForm', () => {
1189
1186
1190
1187
let action ;
1191
1188
function App ( ) {
1192
- const [ state , dispatch ] = useFormState ( ( s , a ) => {
1189
+ const [ state , dispatch , isPending ] = useFormState ( ( s , a ) => {
1193
1190
if ( a . endsWith ( '!' ) ) {
1194
1191
throw new Error ( a ) ;
1195
1192
}
1196
1193
return a ;
1197
1194
} , 'A' ) ;
1198
1195
action = dispatch ;
1199
- return < Text text = { state } /> ;
1196
+ const pending = isPending ? 'Pending ' : '' ;
1197
+ return < Text text = { pending + state } /> ;
1200
1198
}
1201
1199
1202
1200
const root = ReactDOMClient . createRoot ( container ) ;
@@ -1210,7 +1208,13 @@ describe('ReactDOMForm', () => {
1210
1208
assertLog ( [ 'A' ] ) ;
1211
1209
1212
1210
await act ( ( ) => action ( 'Oops!' ) ) ;
1213
- assertLog ( [ 'Caught an error: Oops!' , 'Caught an error: Oops!' ] ) ;
1211
+ assertLog ( [
1212
+ // Action begins, error has not thrown yet.
1213
+ 'Pending A' ,
1214
+ // Now the action runs and throws.
1215
+ 'Caught an error: Oops!' ,
1216
+ 'Caught an error: Oops!' ,
1217
+ ] ) ;
1214
1218
expect ( container . textContent ) . toBe ( 'Caught an error: Oops!' ) ;
1215
1219
1216
1220
// Reset the error boundary
@@ -1223,7 +1227,7 @@ describe('ReactDOMForm', () => {
1223
1227
action ( 'Oops!' ) ;
1224
1228
action ( 'B' ) ;
1225
1229
} ) ;
1226
- assertLog ( [ 'B' ] ) ;
1230
+ assertLog ( [ 'Pending A' , ' B'] ) ;
1227
1231
expect ( container . textContent ) . toBe ( 'B' ) ;
1228
1232
} ) ;
1229
1233
@@ -1247,15 +1251,16 @@ describe('ReactDOMForm', () => {
1247
1251
1248
1252
let action ;
1249
1253
function App ( ) {
1250
- const [ state , dispatch ] = useFormState ( async ( s , a ) => {
1254
+ const [ state , dispatch , isPending ] = useFormState ( async ( s , a ) => {
1251
1255
const text = await getText ( a ) ;
1252
1256
if ( text . endsWith ( '!' ) ) {
1253
1257
throw new Error ( text ) ;
1254
1258
}
1255
1259
return text ;
1256
1260
} , 'A' ) ;
1257
1261
action = dispatch ;
1258
- return < Text text = { state } /> ;
1262
+ const pending = isPending ? 'Pending ' : '' ;
1263
+ return < Text text = { pending + state } /> ;
1259
1264
}
1260
1265
1261
1266
const root = ReactDOMClient . createRoot ( container ) ;
@@ -1269,7 +1274,8 @@ describe('ReactDOMForm', () => {
1269
1274
assertLog ( [ 'A' ] ) ;
1270
1275
1271
1276
await act ( ( ) => action ( 'Oops!' ) ) ;
1272
- assertLog ( [ ] ) ;
1277
+ // The first dispatch will update the pending state.
1278
+ assertLog ( [ 'Pending A' ] ) ;
1273
1279
await act ( ( ) => resolveText ( 'Oops!' ) ) ;
1274
1280
assertLog ( [ 'Caught an error: Oops!' , 'Caught an error: Oops!' ] ) ;
1275
1281
expect ( container . textContent ) . toBe ( 'Caught an error: Oops!' ) ;
@@ -1284,7 +1290,7 @@ describe('ReactDOMForm', () => {
1284
1290
action ( 'Oops!' ) ;
1285
1291
action ( 'B' ) ;
1286
1292
} ) ;
1287
- assertLog ( [ ] ) ;
1293
+ assertLog ( [ 'Pending A' ] ) ;
1288
1294
await act ( ( ) => resolveText ( 'B' ) ) ;
1289
1295
assertLog ( [ 'B' ] ) ;
1290
1296
expect ( container . textContent ) . toBe ( 'B' ) ;
0 commit comments