@@ -1020,15 +1020,15 @@ describe('ReactDOMForm', () => {
10201020 assertLog ( [ '0' ] ) ;
10211021 expect ( container . textContent ) . toBe ( '0' ) ;
10221022
1023- await act ( ( ) => dispatch ( 'increment' ) ) ;
1023+ await act ( ( ) => startTransition ( ( ) => dispatch ( 'increment' ) ) ) ;
10241024 assertLog ( [ 'Async action started [1]' , 'Pending 0' ] ) ;
10251025 expect ( container . textContent ) . toBe ( 'Pending 0' ) ;
10261026
10271027 // Dispatch a few more actions. None of these will start until the previous
10281028 // one finishes.
1029- await act ( ( ) => dispatch ( 'increment' ) ) ;
1030- await act ( ( ) => dispatch ( 'decrement' ) ) ;
1031- await act ( ( ) => dispatch ( 'increment' ) ) ;
1029+ await act ( ( ) => startTransition ( ( ) => dispatch ( 'increment' ) ) ) ;
1030+ await act ( ( ) => startTransition ( ( ) => dispatch ( 'decrement' ) ) ) ;
1031+ await act ( ( ) => startTransition ( ( ) => dispatch ( 'increment' ) ) ) ;
10321032 assertLog ( [ ] ) ;
10331033
10341034 // Each action starts as soon as the previous one finishes.
@@ -1067,7 +1067,7 @@ describe('ReactDOMForm', () => {
10671067
10681068 // Perform an action. This will increase the state by 1, as defined by the
10691069 // stepSize prop.
1070- await act ( ( ) => increment ( ) ) ;
1070+ await act ( ( ) => startTransition ( ( ) => increment ( ) ) ) ;
10711071 assertLog ( [ 'Pending 0' , '1' ] ) ;
10721072
10731073 // Now increase the stepSize prop to 10. Subsequent steps will increase
@@ -1076,7 +1076,7 @@ describe('ReactDOMForm', () => {
10761076 assertLog ( [ '1' ] ) ;
10771077
10781078 // Increment again. The state should increase by 10.
1079- await act ( ( ) => increment ( ) ) ;
1079+ await act ( ( ) => startTransition ( ( ) => increment ( ) ) ) ;
10801080 assertLog ( [ 'Pending 1' , '11' ] ) ;
10811081 } ) ;
10821082
@@ -1113,11 +1113,11 @@ describe('ReactDOMForm', () => {
11131113 await act ( ( ) => root . render ( < App /> ) ) ;
11141114 assertLog ( [ 'A' ] ) ;
11151115
1116- await act ( ( ) => action ( 'B' ) ) ;
1116+ await act ( ( ) => startTransition ( ( ) => action ( 'B' ) ) ) ;
11171117 // The first dispatch will update the pending state.
11181118 assertLog ( [ 'Pending A' ] ) ;
1119- await act ( ( ) => action ( 'C' ) ) ;
1120- await act ( ( ) => action ( 'D' ) ) ;
1119+ await act ( ( ) => startTransition ( ( ) => action ( 'C' ) ) ) ;
1120+ await act ( ( ) => startTransition ( ( ) => action ( 'D' ) ) ) ;
11211121 assertLog ( [ ] ) ;
11221122
11231123 await act ( ( ) => resolveText ( 'B' ) ) ;
@@ -1151,10 +1151,10 @@ describe('ReactDOMForm', () => {
11511151
11521152 // Dispatch two actions. The first one is async, so it forces the second
11531153 // one into an async queue.
1154- await act ( ( ) => action ( 'First action' ) ) ;
1154+ await act ( ( ) => startTransition ( ( ) => action ( 'First action' ) ) ) ;
11551155 assertLog ( [ 'Initial (pending)' ] ) ;
11561156 // This action won't run until the first one finishes.
1157- await act ( ( ) => action ( 'Second action' ) ) ;
1157+ await act ( ( ) => startTransition ( ( ) => action ( 'Second action' ) ) ) ;
11581158
11591159 // While the first action is still pending, update a prop. This causes the
11601160 // inline action implementation to change, but it should not affect the
@@ -1169,7 +1169,9 @@ describe('ReactDOMForm', () => {
11691169
11701170 // Confirm that if we dispatch yet another action, it uses the updated
11711171 // action implementation.
1172- await expect ( act ( ( ) => action ( 'Third action' ) ) ) . rejects . toThrow ( 'Oops!' ) ;
1172+ await expect (
1173+ act ( ( ) => startTransition ( ( ) => action ( 'Third action' ) ) ) ,
1174+ ) . rejects . toThrow ( 'Oops!' ) ;
11731175 } ,
11741176 ) ;
11751177
@@ -1192,7 +1194,7 @@ describe('ReactDOMForm', () => {
11921194
11931195 // Perform an action. This will increase the state by 1, as defined by the
11941196 // stepSize prop.
1195- await act ( ( ) => increment ( ) ) ;
1197+ await act ( ( ) => startTransition ( ( ) => increment ( ) ) ) ;
11961198 assertLog ( [ 'Pending 0' , '1' ] ) ;
11971199
11981200 // Now increase the stepSize prop to 10. Subsequent steps will increase
@@ -1201,7 +1203,7 @@ describe('ReactDOMForm', () => {
12011203 assertLog ( [ '1' ] ) ;
12021204
12031205 // Increment again. The state should increase by 10.
1204- await act ( ( ) => increment ( ) ) ;
1206+ await act ( ( ) => startTransition ( ( ) => increment ( ) ) ) ;
12051207 assertLog ( [ 'Pending 1' , '11' ] ) ;
12061208 } ) ;
12071209
@@ -1219,12 +1221,12 @@ describe('ReactDOMForm', () => {
12191221 await act ( ( ) => root . render ( < App /> ) ) ;
12201222 assertLog ( [ 'A' ] ) ;
12211223
1222- await act ( ( ) => action ( getText ( 'B' ) ) ) ;
1224+ await act ( ( ) => startTransition ( ( ) => action ( getText ( 'B' ) ) ) ) ;
12231225 // The first dispatch will update the pending state.
12241226 assertLog ( [ 'Pending A' ] ) ;
1225- await act ( ( ) => action ( 'C' ) ) ;
1226- await act ( ( ) => action ( getText ( 'D' ) ) ) ;
1227- await act ( ( ) => action ( 'E' ) ) ;
1227+ await act ( ( ) => startTransition ( ( ) => action ( 'C' ) ) ) ;
1228+ await act ( ( ) => startTransition ( ( ) => action ( getText ( 'D' ) ) ) ) ;
1229+ await act ( ( ) => startTransition ( ( ) => action ( 'E' ) ) ) ;
12281230 assertLog ( [ ] ) ;
12291231
12301232 await act ( ( ) => resolveText ( 'B' ) ) ;
@@ -1273,7 +1275,7 @@ describe('ReactDOMForm', () => {
12731275 ) ;
12741276 assertLog ( [ 'A' ] ) ;
12751277
1276- await act ( ( ) => action ( 'Oops!' ) ) ;
1278+ await act ( ( ) => startTransition ( ( ) => action ( 'Oops!' ) ) ) ;
12771279 assertLog ( [
12781280 // Action begins, error has not thrown yet.
12791281 'Pending A' ,
@@ -1290,8 +1292,8 @@ describe('ReactDOMForm', () => {
12901292 // Trigger an error again, but this time, perform another action that
12911293 // overrides the first one and fixes the error
12921294 await act ( ( ) => {
1293- action ( 'Oops!' ) ;
1294- action ( 'B' ) ;
1295+ startTransition ( ( ) => action ( 'Oops!' ) ) ;
1296+ startTransition ( ( ) => action ( 'B' ) ) ;
12951297 } ) ;
12961298 assertLog ( [ 'Pending A' , 'B' ] ) ;
12971299 expect ( container . textContent ) . toBe ( 'B' ) ;
@@ -1338,7 +1340,7 @@ describe('ReactDOMForm', () => {
13381340 ) ;
13391341 assertLog ( [ 'A' ] ) ;
13401342
1341- await act ( ( ) => action ( 'Oops!' ) ) ;
1343+ await act ( ( ) => startTransition ( ( ) => action ( 'Oops!' ) ) ) ;
13421344 // The first dispatch will update the pending state.
13431345 assertLog ( [ 'Pending A' ] ) ;
13441346 await act ( ( ) => resolveText ( 'Oops!' ) ) ;
@@ -1352,8 +1354,8 @@ describe('ReactDOMForm', () => {
13521354 // Trigger an error again, but this time, perform another action that
13531355 // overrides the first one and fixes the error
13541356 await act ( ( ) => {
1355- action ( 'Oops!' ) ;
1356- action ( 'B' ) ;
1357+ startTransition ( ( ) => action ( 'Oops!' ) ) ;
1358+ startTransition ( ( ) => action ( 'B' ) ) ;
13571359 } ) ;
13581360 assertLog ( [ 'Pending A' ] ) ;
13591361 await act ( ( ) => resolveText ( 'B' ) ) ;
@@ -1399,7 +1401,7 @@ describe('ReactDOMForm', () => {
13991401 assertLog ( [ '0' ] ) ;
14001402 expect ( container . textContent ) . toBe ( '0' ) ;
14011403
1402- await act ( ( ) => dispatch ( 'increment' ) ) ;
1404+ await act ( ( ) => startTransition ( ( ) => dispatch ( 'increment' ) ) ) ;
14031405 assertLog ( [ 'Async action started [1]' , 'Pending 0' ] ) ;
14041406 expect ( container . textContent ) . toBe ( 'Pending 0' ) ;
14051407
@@ -1408,6 +1410,77 @@ describe('ReactDOMForm', () => {
14081410 expect ( container . textContent ) . toBe ( '1' ) ;
14091411 } ) ;
14101412
1413+ test ( 'useActionState does not wrap action in a transition unless dispatch is in a transition' , async ( ) => {
1414+ let dispatch ;
1415+ function App ( ) {
1416+ const [ state , _dispatch ] = useActionState ( ( ) => {
1417+ return state + 1 ;
1418+ } , 0 ) ;
1419+ dispatch = _dispatch ;
1420+ return < AsyncText text = { 'Count: ' + state } /> ;
1421+ }
1422+
1423+ const root = ReactDOMClient . createRoot ( container ) ;
1424+ await act ( ( ) =>
1425+ root . render (
1426+ < Suspense fallback = { < Text text = "Loading..." /> } >
1427+ < App />
1428+ </ Suspense > ,
1429+ ) ,
1430+ ) ;
1431+ assertLog ( [ 'Suspend! [Count: 0]' , 'Loading...' ] ) ;
1432+ await act ( ( ) => resolveText ( 'Count: 0' ) ) ;
1433+ assertLog ( [ 'Count: 0' ] ) ;
1434+
1435+ // Dispatch outside of a transition. This will trigger a loading state.
1436+ await act ( ( ) => dispatch ( ) ) ;
1437+ assertLog ( [ 'Suspend! [Count: 1]' , 'Loading...' ] ) ;
1438+ expect ( container . textContent ) . toBe ( 'Loading...' ) ;
1439+
1440+ await act ( ( ) => resolveText ( 'Count: 1' ) ) ;
1441+ assertLog ( [ 'Count: 1' ] ) ;
1442+ expect ( container . textContent ) . toBe ( 'Count: 1' ) ;
1443+
1444+ // Now dispatch inside of a transition. This one does not trigger a
1445+ // loading state.
1446+ await act ( ( ) => startTransition ( ( ) => dispatch ( ) ) ) ;
1447+ assertLog ( [ 'Count: 1' , 'Suspend! [Count: 2]' , 'Loading...' ] ) ;
1448+ expect ( container . textContent ) . toBe ( 'Count: 1' ) ;
1449+
1450+ await act ( ( ) => resolveText ( 'Count: 2' ) ) ;
1451+ assertLog ( [ 'Count: 2' ] ) ;
1452+ expect ( container . textContent ) . toBe ( 'Count: 2' ) ;
1453+ } ) ;
1454+
1455+ test ( 'useActionState warns if async action is dispatched outside of a transition' , async ( ) => {
1456+ let dispatch ;
1457+ function App ( ) {
1458+ const [ state , _dispatch ] = useActionState ( async ( ) => {
1459+ return state + 1 ;
1460+ } , 0 ) ;
1461+ dispatch = _dispatch ;
1462+ return < AsyncText text = { 'Count: ' + state } /> ;
1463+ }
1464+
1465+ const root = ReactDOMClient . createRoot ( container ) ;
1466+ await act ( ( ) => root . render ( < App /> ) ) ;
1467+ assertLog ( [ 'Suspend! [Count: 0]' ] ) ;
1468+ await act ( ( ) => resolveText ( 'Count: 0' ) ) ;
1469+ assertLog ( [ 'Count: 0' ] ) ;
1470+
1471+ // Dispatch outside of a transition.
1472+ await act ( ( ) => dispatch ( ) ) ;
1473+ assertConsoleErrorDev ( [
1474+ [
1475+ 'An async function was passed to useActionState, but it was ' +
1476+ 'dispatched outside of an action context' ,
1477+ { withoutStack : true } ,
1478+ ] ,
1479+ ] ) ;
1480+ assertLog ( [ 'Suspend! [Count: 1]' ] ) ;
1481+ expect ( container . textContent ) . toBe ( 'Count: 0' ) ;
1482+ } ) ;
1483+
14111484 test ( 'uncontrolled form inputs are reset after the action completes' , async ( ) => {
14121485 const formRef = React . createRef ( ) ;
14131486 const inputRef = React . createRef ( ) ;
0 commit comments