Skip to content
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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Warn when "false" or "true" is the value of a boolean DOM prop #13372

Merged
merged 4 commits into from Aug 14, 2018
Merged

Warn when "false" or "true" is the value of a boolean DOM prop #13372

merged 4 commits into from Aug 14, 2018

Conversation

@motiz88
Copy link
Contributor

@motiz88 motiz88 commented Aug 12, 2018

Hi! 馃槃

This is my first contribution that actually changes runtime behaviour, and involved some guesswork as to where to make what change to get the desired effect - so do take it with a grain of salt. I'm super willing to learn more about how React is written and fix anything that needs to be fixed in this PR, if the functionality is welcome.


This PR adds a new DEV-mode warning when the value "false" (case-insensitive) is found in a DOM prop of type BOOLEAN, recommending the use of the boolean value false instead.

While working on facebook/flow#6727 I came across the different types of boolean DOM props, and specifically the fact that BOOLEAN props (e.g. hidden) treat the string "false" as equivalent to boolean true, whereas BOOLEANISH_STRING props (e.g. spellCheck) treat it as equivalent to boolean false - an inconsistency that follows from the HTML spec, but one that can conceivably trip up developers, hence a warning.

I'm not entirely sure what OVERLOADED_BOOLEAN props should do about "false" - currently the warning doesn't apply to them.

EDIT: Per feedback below, this now also warns on "true", and only warns on case-sensitive (lowercase) matches.

@pull-bot
Copy link

@pull-bot pull-bot commented Aug 12, 2018

Details of bundled changes.

Comparing: 725e499...f70a0eb

react-dom

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-dom.development.js +0.1% +0.1% 641.27 KB 641.81 KB 150.69 KB 150.86 KB UMD_DEV
react-dom.development.js +0.1% +0.1% 637.41 KB 637.95 KB 149.53 KB 149.69 KB NODE_DEV
react-dom-server.browser.development.js +0.5% +0.7% 103.08 KB 103.62 KB 27.49 KB 27.67 KB UMD_DEV
react-dom-server.browser.development.js +0.5% +0.6% 99.21 KB 99.75 KB 26.54 KB 26.7 KB NODE_DEV
react-dom-server.node.development.js +0.5% +0.6% 101.13 KB 101.67 KB 27.07 KB 27.23 KB NODE_DEV
ReactDOM-dev.js +0.1% +0.1% 644.54 KB 645.21 KB 147.76 KB 147.93 KB FB_WWW_DEV
ReactDOMServer-dev.js +0.7% +0.6% 100.31 KB 100.98 KB 26.25 KB 26.42 KB FB_WWW_DEV

Generated by 馃毇 dangerJS

Loading

it('warns on the ambiguous string value "False"', function() {
let el;
expect(() => {
el = ReactTestUtils.renderIntoDocument(<div hidden="False" />);
Copy link
Member

@gaearon gaearon Aug 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since title case False doesn鈥檛 have special meaning I don鈥檛 think it鈥檚 worth testing for.

Loading

Copy link
Contributor Author

@motiz88 motiz88 Aug 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy to take your steer on this, I'd just like to state my case first for completeness: The thing I was trying to make safer was people drawing inferences from one "boolean" HTML attribute to another. Since enumerated attribute values are case-insensitive, spellCheck="False" does behave differently to hidden="False", in exactly the same way as the respective lowercase variants do. I take your point that most people would be using lowercase to begin with, so this is probably a rarer case, but it does theoretically occur.

To clarify, you're referring to dropping the toLowerCase() in the actual check, as well as removing this test, right?

Loading

Copy link
Member

@gaearon gaearon Aug 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. I see your point but I think it's unlikely enough to be written accidentally and we want to make the checks as cheap as possible. Strict comparison is better than extra toLowerCase call.

Loading

propertyInfo.type === BOOLEAN &&
typeof value === 'string' &&
value.toLowerCase() === 'false'
) {
Copy link
Member

@gaearon gaearon Aug 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let鈥檚 make value === 'false' the first condition. Since it鈥檚 rare we won鈥檛 have to check most other conditions.

Loading

@gaearon
Copy link
Member

@gaearon gaearon commented Aug 12, 2018

I think this makes sense overall. Maybe with warning for "true" too? You might be getting it from an API and only have actual "false" in production. But this would tell you early.

Loading

@@ -444,7 +444,7 @@ describe('ReactDOMInput', () => {

it('should take `defaultValue` when changing to uncontrolled input', () => {
const node = ReactDOM.render(
<input type="text" value="0" readOnly="true" />,
<input type="text" value="0" readOnly={true} />,
Copy link
Contributor Author

@motiz88 motiz88 Aug 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test was broken by adding the warning on "true". I opted to just fix what the warning was complaining about, which doesn't change the semantics of this test AFAICT.

Loading

@motiz88 motiz88 changed the title Warn when the string "false" is the value of a boolean DOM prop Warn when "false" or "true" is the value of a boolean DOM prop Aug 12, 2018
@motiz88
Copy link
Contributor Author

@motiz88 motiz88 commented Aug 12, 2018

@gaearon Thanks for the review! All feedback addressed.

Loading

expect(() => {
el = ReactTestUtils.renderIntoDocument(<div hidden="true" />);
}).toWarnDev(
'Received the string `true` for the boolean attribute `hidden`. ' +
Copy link
Member

@gaearon gaearon Aug 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add Although this works, it will not work as expected if you pass the string "false". to the true case.

Loading

@motiz88
Copy link
Contributor Author

@motiz88 motiz88 commented Aug 14, 2018

@gaearon Friendly ping 馃槃 I've amended the warning messages as per your feedback. Anything else you'd like to see here?

Loading

@gaearon gaearon merged commit 2c59076 into facebook:master Aug 14, 2018
1 check passed
Loading
@gaearon
Copy link
Member

@gaearon gaearon commented Aug 14, 2018

Looks great. Thank you.

Loading

@AlexGalays
Copy link

@AlexGalays AlexGalays commented Aug 21, 2018

This is dead code when using a type system. Could this be enabled only in dev mode perhaps?

Loading

@AlexGalays
Copy link

@AlexGalays AlexGalays commented Aug 21, 2018

Nevermind, didn't see the if (__DEV) :)

Loading

TejasQ pushed a commit to TejasQ/react that referenced this issue Aug 26, 2018
鈥ok#13372)

* Warn when the string "false" is the value of a boolean DOM prop

* Only warn on exact case match for "false" in DOM boolean props

* Warn on string "true" as well as "false" in DOM boolean props

* Clarify warnings on "true" / "false" values in DOM boolean props
@gaearon gaearon mentioned this pull request Sep 5, 2018
@veekas
Copy link
Contributor

@veekas veekas commented Oct 20, 2018

Thanks for this PR, @motiz88. It surfaced a 1+ year-old bug in a codebase I work on. 馃憤

Loading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

6 participants