-
Notifications
You must be signed in to change notification settings - Fork 47k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Avoid setting empty value on reset & submit inputs #12780
Conversation
// Submit or reset inputs should not have their value overridden | ||
// with a falsy value, or else this will result in a button with no text, | ||
// ignoring the browser-default "Submit", etc. | ||
if (!props.value && (props.type === 'submit' || props.type === 'reset')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would make it impossible to intentionally set an empty value
<input type="submit" value="" />
We'd need a more specific check like props.value === undefined
. It should probably be moved into the hasOwnProperty
check below, so we can avoid checking type
if value
doesn't exist at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we cater to null
in the same way? If we want to match React 15 behavior, anyway.
|
||
expect(node.getAttribute('value')).toBe('banana'); | ||
}); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a test for an intentionally empty value?
ReactDOM: size: 🔺+0.2%, gzip: 🔺+0.2% Details of bundled changes.Comparing: 8862172...3fa437a react-dom
Generated by 🚫 dangerJS |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Returning is not sufficient.
This doesn't work if you first render with value="Foo"
and then render with value={undefined}
because it will just fail to update, and incorrectly keep "Foo"
as the value.
const node = ReactDOM.findDOMNode(stub); | ||
|
||
expect( | ||
!node.hasAttribute('value') || node.getAttribute('value').length > 0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is confusing. Which one of the two do you expect in jsdom environment? Please be specific. We don't run these tests in browsers so you don't need to account for different behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Heh, good spot. This is a bit of copy-pasta from the tests above. Will fix.
@gaearon Would you have any examples of tests where we handle that type of situation? Otherwise I'm more or less working in the dark, because we don't seem to cover that situation on any input field tests. |
You can take the test you added and render twice. |
We also need to consider the |
Returning early is sufficient for mounting, we just need to handle updates as well. @ellsclytn updates are handled by react/packages/react-dom/src/client/ReactDOMFiberInput.js Lines 183 to 195 in c601f7a
Since this already checks for if (value != null) {
// ...
} else if (props.type === 'submit' || props.type === 'reset') {
node.removeAttribute('value');
return;
} I tested this locally and verified that updates to and from |
I think we should consider react/packages/react-dom/src/client/ReactDOMFiberInput.js Lines 37 to 40 in c601f7a
|
@gebilaoxiong we still want the warning to occur in this case. |
Thanks for the advice all! Made the appropriate changes, tested re-rendering with changing props, looks to be behaving properly. |
@@ -209,6 +214,16 @@ export function postMountWrapper(element: Element, props: Object) { | |||
const node = ((element: any): InputWithWrapperState); | |||
|
|||
if (props.hasOwnProperty('value') || props.hasOwnProperty('defaultValue')) { | |||
// Submit or reset inputs should not have their value overridden | |||
// with a falsy value, or else this will result in a button with no text, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This says "falsy" but the code checks for null
and undefined
. What should happen for ''
? For 0
? For false
or true
? Let's make sure code and comments agree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good spot. Fixed 😄
@aweary Are you good with this fix? |
} else if (props.type === 'submit' || props.type === 'reset') { | ||
// Submit/reset inputs need the attribute removed completely to avoid | ||
// blank-text buttons. | ||
node.removeAttribute('value'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a bit surprising this is the only place we use removeAttribute
in this file.
@@ -182,6 +182,14 @@ export function updateWrapper(element: Element, props: Object) { | |||
} else if (node.value !== '' + value) { | |||
node.value = '' + value; | |||
} | |||
} else if ( | |||
(props.type === 'submit' || props.type === 'reset') && | |||
(value === null || value === undefined) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This condition seems unnecessary because we're in an else
branch for if (value != null)
.
) { | ||
// Submit/reset inputs need the attribute removed completely to avoid | ||
// blank-text buttons. | ||
node.removeAttribute('value'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I remove this line none of the tests seem to fail. Please make sure the tests cover all lines.
@@ -182,6 +182,14 @@ export function updateWrapper(element: Element, props: Object) { | |||
} else if (node.value !== '' + value) { | |||
node.value = '' + value; | |||
} | |||
} else if ( | |||
(props.type === 'submit' || props.type === 'reset') && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: since we access props.type
so much can you please read type
in a variable early and use that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see review comments above. We need to make sure that any new lines are covered by tests that would fail were these lines removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Solution looks fine to me, just some minor nits. We're only testing for submit
inputs, but this also affects reset
inputs. It'd be nice to test that as well to prevent regressions.
@@ -203,6 +209,16 @@ export function postMountWrapper( | |||
const node = ((element: any): InputWithWrapperState); | |||
|
|||
if (props.hasOwnProperty('value') || props.hasOwnProperty('defaultValue')) { | |||
// Submit or reset inputs should not have their value overridden | |||
// with a null or undefined value, or else this will result in a button with | |||
// no text, ignoring the browser-default "Submit", etc. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expect(node.getAttribute('value')).toBe(null); | ||
}); | ||
|
||
it('should set a value to a submit input', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
set a value *on a submit input
expect(node.hasAttribute('value')).toBe(false); | ||
}); | ||
|
||
it('should not set a value for submit buttons when undefined is supplied', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a better name would be:
"should remove the value attribute on submit inputs when value is updated to undefined"
@aweary Oops, I totally forgot about the |
// no text, ignoring the browser-default "Submit", etc. | ||
if ( | ||
(props.value === undefined || props.value === null) && | ||
(props.type === 'submit' || props.type === 'reset') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's put this line first? It'll probably be false
more often.
Thank you! |
This fixes a regression of #7179. Previously, creating an input with
type='submit' value={undefined}
would result in a Submit button with the text using the browser default for the input type (usually "Submit", for en). Currently, passing throughundefined
results in an input with no text at all. The same behaviour can be demonstrated withtype='reset'
.Resolves #12872