Skip to content

Commit 856dc5e

Browse files
authored
Fix escaping in action error URL (#27273)
This URL is generated on the client (there's an equivalent but shorter SSR version too) when a function is used as an action. It should never happen but it'll be invoked if a form is manually submitted or event is stopped early. The `'` wasn't escaped so this yielded invalid syntax. Which is an error too but much less helpful. `missing ) after argument list`. Added a test that evals to make sure it's correct syntax.
1 parent 31034b6 commit 856dc5e

File tree

2 files changed

+48
-1
lines changed

2 files changed

+48
-1
lines changed

packages/react-dom-bindings/src/client/ReactDOMComponent.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,7 @@ function setProp(
501501
// eslint-disable-next-line no-script-url
502502
"javascript:throw new Error('" +
503503
'A React form was unexpectedly submitted. If you called form.submit() manually, ' +
504-
"consider using form.requestSubmit() instead. If you're trying to use " +
504+
"consider using form.requestSubmit() instead. If you\\'re trying to use " +
505505
'event.stopPropagation() in a submit event handler, consider also calling ' +
506506
'event.preventDefault().' +
507507
"')",

packages/react-dom/src/__tests__/ReactDOMForm-test.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -922,4 +922,51 @@ describe('ReactDOMForm', () => {
922922
await act(() => resolveText('Wait'));
923923
assertLog(['Async action finished', 'No pending action']);
924924
});
925+
926+
// @gate enableFormActions
927+
it('should error if submitting a form manually', async () => {
928+
const ref = React.createRef();
929+
930+
let error = null;
931+
let result = null;
932+
933+
function emulateForceSubmit(submitter) {
934+
const form = submitter.form || submitter;
935+
const action =
936+
(submitter && submitter.getAttribute('formaction')) || form.action;
937+
try {
938+
if (!/\s*javascript:/i.test(action)) {
939+
throw new Error('Navigate to: ' + action);
940+
} else {
941+
// eslint-disable-next-line no-new-func
942+
result = Function(action.slice(11))();
943+
}
944+
} catch (x) {
945+
error = x;
946+
}
947+
}
948+
949+
const root = ReactDOMClient.createRoot(container);
950+
await act(async () => {
951+
root.render(
952+
<form
953+
action={() => {}}
954+
ref={ref}
955+
onSubmit={e => {
956+
e.preventDefault();
957+
emulateForceSubmit(e.target);
958+
}}>
959+
<input type="text" name="foo" defaultValue="bar" />
960+
</form>,
961+
);
962+
});
963+
964+
// This submits the form, which gets blocked and then resubmitted. It's a somewhat
965+
// common idiom but we don't support this pattern unless it uses requestSubmit().
966+
await submit(ref.current);
967+
expect(result).toBe(null);
968+
expect(error.message).toContain(
969+
'A React form was unexpectedly submitted. If you called form.submit()',
970+
);
971+
});
925972
});

0 commit comments

Comments
 (0)