Allow empty string to be passed to formAction (#26379)
We disallow empty strings for `href` and `src` since they're common mistakes that end up loading the current page as a preload, image or link. We also disallow it for `action`. You have to pass `null` which is the same. However, for `formAction` passing `null` is not the same as passing empty string. Passing empty string overrides the form's action to be the current page even if the form's action was set to something else. There's no easy way to express the same thing `#` show up in the user visible URLs and `?` clears the search params. Since this is also not a common mistake, we can just allow this.
Sebastian Markbåge committed
Mar 13, 2023 at 14:28 UTC
2788d0d8dd95ed57e2dbb148853fe69010e992fe
3 files changed
+23
-23
packages/react-dom-bindings/src/shared/DOMProperty.js
+13
-1
@@ -636,7 +636,19 @@ properties[xlinkHref] = new PropertyInfoRecord(
636
false, // removeEmptyString
637
);
638
639
-['src', 'href', 'action', 'formAction'].forEach(attributeName => {
639
+const formAction = 'formAction';
640
+// $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions
641
+properties[formAction] = new PropertyInfoRecord(
642
+ 'formAction',
643
+ STRING,
644
+ false, // mustUseProperty
645
+ 'formaction', // attributeName
646
+ null, // attributeNamespace
647
+ true, // sanitizeURL
648
+ false, // removeEmptyString
649
+);
650
+
651
+['src', 'href', 'action'].forEach(attributeName => {
652
// $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions
653
properties[attributeName] = new PropertyInfoRecord(
654
attributeName,
packages/react-dom/src/__tests__/ReactDOMComponent-test.js
+9
-21
@@ -533,29 +533,17 @@ describe('ReactDOMComponent', () => {
533
expect(node.hasAttribute('action')).toBe(false);
534
});
535
536
- it('should not add an empty formAction attribute', () => {
536
+ it('allows empty string of a formAction to override the default of a parent', () => {
537
const container = document.createElement('div');
538
- expect(() =>
539
- ReactDOM.render(<button formAction="" />, container),
540
- ).toErrorDev(
541
- 'An empty string ("") was passed to the formAction attribute. ' +
542
- 'To fix this, either do not render the element at all ' +
543
- 'or pass null to formAction instead of an empty string.',
544
- );
545
- const node = container.firstChild;
546
- expect(node.hasAttribute('formAction')).toBe(false);
547
-
548
- ReactDOM.render(<button formAction="abc" />, container);
549
- expect(node.hasAttribute('formAction')).toBe(true);
550
-
551
- expect(() =>
552
- ReactDOM.render(<button formAction="" />, container),
553
- ).toErrorDev(
554
- 'An empty string ("") was passed to the formAction attribute. ' +
555
- 'To fix this, either do not render the element at all ' +
556
- 'or pass null to formAction instead of an empty string.',
538
+ ReactDOM.render(
539
+ <form action="hello">
540
+ <button formAction="" />,
541
+ </form>,
542
+ container,
543
);
558
- expect(node.hasAttribute('formAction')).toBe(false);
544
+ const node = container.firstChild.firstChild;
545
+ expect(node.hasAttribute('formaction')).toBe(true);
546
+ expect(node.getAttribute('formaction')).toBe('');
547
});
548
549
it('should not filter attributes for custom elements', () => {
packages/shared/ReactFeatureFlags.js
+1
-1
@@ -172,7 +172,7 @@ export const disableInputAttributeSyncing = false;
172
// Filter certain DOM attributes (e.g. src, href) if their values are empty
173
// strings. This prevents e.g. <img src=""> from making an unnecessary HTTP
174
// request for certain browsers.
175
-export const enableFilterEmptyStringAttributesDOM = false;
175
+export const enableFilterEmptyStringAttributesDOM = __EXPERIMENTAL__;
176
177
// Changes the behavior for rendering custom elements in both server rendering
178
// and client rendering, mostly to allow JSX attributes to apply to the custom