@samitouri / QOS-React-1 / commits / b8e47d988e

Bugfix: useFormState queues actions in wrong order (#27570)

I neglected to update the "last" pointer of the action queue. Since the queue is circular, rather than dropping the update, the effect was to add the update to the front of the queue instead of the back. I didn't notice earlier because in my demos/tests, the actions would either resolve really quickly or the actions weren't order dependent (like incrementing a counter).

Andrew Clark committed Oct 23, 2023 at 14:52 UTC b8e47d988eb3ba547c102c0b12c351250ed955e0
2 files changed +31 -2
packages/react-dom/src/__tests__/ReactDOMForm-test.js
+30 -1
@@ -90,7 +90,7 @@ describe('ReactDOMForm', () => {
90 const thenable = record.value;
91 record.status = 'resolved';
92 record.value = text;
93 - thenable.pings.forEach(t => t());
93 + thenable.pings.forEach(t => t(text));
94 }
95 }
96
@@ -1082,6 +1082,35 @@ describe('ReactDOMForm', () => {
1082 });
1083 });
1084
1085 + // @gate enableFormActions
1086 + // @gate enableAsyncActions
1087 + test('queues multiple actions and runs them in order', async () => {
1088 + let action;
1089 + function App() {
1090 + const [state, dispatch] = useFormState(
1091 + async (s, a) => await getText(a),
1092 + 'A',
1093 + );
1094 + action = dispatch;
1095 + return <Text text={state} />;
1096 + }
1097 +
1098 + const root = ReactDOMClient.createRoot(container);
1099 + await act(() => root.render(<App />));
1100 + assertLog(['A']);
1101 +
1102 + await act(() => action('B'));
1103 + await act(() => action('C'));
1104 + await act(() => action('D'));
1105 +
1106 + await act(() => resolveText('B'));
1107 + await act(() => resolveText('C'));
1108 + await act(() => resolveText('D'));
1109 +
1110 + assertLog(['D']);
1111 + expect(container.textContent).toBe('D');
1112 + });
1113 +
1114 // @gate enableFormActions
1115 // @gate enableAsyncActions
1116 test('useFormState: warns if action is not async', async () => {
packages/react-reconciler/src/ReactFiberHooks.js
+1 -1
@@ -1915,7 +1915,7 @@ function dispatchFormState<S, P>(
1915 payload,
1916 next: first,
1917 };
1918 - last.next = newLast;
1918 + actionQueue.pending = last.next = newLast;
1919 }
1920 }
1921