@samitouri / QOS-React-1 / commits / 856dc5e433

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.

Sebastian Markbåge committed Aug 22, 2023 at 19:10 UTC 856dc5e43352454b1d06c13e233d565ceda77e61
2 files changed +48 -1
packages/react-dom-bindings/src/client/ReactDOMComponent.js
+1 -1
@@ -501,7 +501,7 @@ function setProp(
501 // eslint-disable-next-line no-script-url
502 "javascript:throw new Error('" +
503 '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 " +
505 'event.stopPropagation() in a submit event handler, consider also calling ' +
506 'event.preventDefault().' +
507 "')",
packages/react-dom/src/__tests__/ReactDOMForm-test.js
+47
@@ -922,4 +922,51 @@ describe('ReactDOMForm', () => {
922 await act(() => resolveText('Wait'));
923 assertLog(['Async action finished', 'No pending action']);
924 });
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 + });
972 });