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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for using !important in inline style values #12181

Conversation

necolas
Copy link
Contributor

@necolas necolas commented Feb 8, 2018

Proposed solution for supporting the !important priority for inline styles (#1881). I couldn't write a test for this change; it looks like jsdom doesn't fully support the browser APIs being used. Please let me know if you've got any ideas.

I used RNW's early-warning benchmarks to check how this change affects the inline-styles timings in Chrome. I couldn't see any difference when styles don't use !important. There is a 10-15% increase in time taken when every node in the tree (>600) has at least one style value using !important and going through the new code path. Overall it looks like it avoids introducing a performance regression for existing using of style while enabling use of !important if needed.

A micro-optimization PR for hyphenateStyleName in fbjs helps a little too.

(This patch is related to #12179, which adds a warning when !important is used.)

@aweary
Copy link
Contributor

aweary commented Feb 8, 2018

I used RNW's early-warning benchmarks to check how this change affects the inline-styles timings in Chrome. I couldn't see any difference when styles don't use !important

@necolas is there any way you could publish the benchmarks with this change? I'm curious how it performs on lower-end mobile devices as well.

@necolas
Copy link
Contributor Author

necolas commented Feb 8, 2018

No problem: https://necolas.github.io/react-native-web/benchmarks/react-patch/

I've tested on a Moto G4 and a 2011 MBP so far.

The React implementation is React 16.2 + this patch.

The inline-style implementation is using plain inline styles. For a baseline reference, you can run the same inline-style test using React 16.2 here. The results look the same to me.

The inline-style-important implementation is exactly the same as inline-style, except every style value includes the !important priority. That's about 630 elements setting 12-16 style properties each. Pretty extreme and takes >50% longer to complete the mount tests (I'm assuming style.setProperty is a slower browser API), but it renders correctly.

@necolas
Copy link
Contributor Author

necolas commented Feb 12, 2018

Do you think this feature will make it into React 16.3?

necolas added a commit to necolas/react-native-web that referenced this pull request Feb 12, 2018
Updates the 'setValueForStyle' implementation to support style values
that contain "!important". This allows the 'flex{Basis,Grow,Shrink}'
values created by the style resolver to be applied. They currently use
the important priority as a work-around for browser-inconsistencies in
the 'flex' shorthand.

Upstream fix: facebook/react#12181

Ref #798
@necolas necolas force-pushed the necolas/support-important-inline-styles branch from 175599b to 7cc1ff4 Compare February 14, 2018 20:33
necolas added a commit to necolas/react-native-web that referenced this pull request Feb 14, 2018
Updates the 'setValueForStyle' implementation to support style values
that contain "!important". This allows the 'flex{Basis,Grow,Shrink}'
values created by the style resolver to be applied. They currently use
the important priority as a work-around for browser-inconsistencies in
the 'flex' shorthand.

Upstream fix: facebook/react#12181

Ref #798
necolas added a commit to necolas/react-native-web that referenced this pull request Feb 14, 2018
Updates the 'setValueForStyle' implementation to support style values
that contain "!important". This allows the 'flex{Basis,Grow,Shrink}'
values created by the style resolver to be applied. They currently use
the important priority as a work-around for browser-inconsistencies in
the 'flex' shorthand.

Upstream fix: facebook/react#12181

Ref #798
Close #813
@aweary aweary requested a review from gaearon February 14, 2018 23:57
Copy link
Contributor

@bvaughn bvaughn left a comment

Choose a reason for hiding this comment

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

This change looks reasonable to me.

I wish there was a way to test it, but I jsdom doesn't seem to support it properly as you say.

@bvaughn
Copy link
Contributor

bvaughn commented Mar 22, 2018

I'll defer the final decision on this to @sophiebits because I am not familiar with this part of the codebase.

@gaearon has expressed concerns that this could be considered a breaking change. (Personally, I feel it's more of a bug fix but I also see his POV.)

@sebmarkbage may also have opinions about this wrt future CSS APIs. (He's on PTO though so I wouldn't expect a response from him at the moment.)

@bvaughn bvaughn requested a review from sophiebits March 22, 2018 21:23
Copy link
Contributor

@aweary aweary left a comment

Choose a reason for hiding this comment

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

The benchmarks and implementation look good to me 👍

@gaearon
Copy link
Collaborator

gaearon commented Mar 22, 2018

To clarify what I meant a little bit.

A bug fix and a breaking change are not mutually exclusive. Sometimes we release a breaking bugfix in a patch. But this is rare and we only do this when it worked just a minor or a patch ago, and we know we recently messed up and the cost of not patching it up is high.

In this particular case the updates never worked, and the initial render worked for the last time in 0.14. So this hasn’t worked at all for about two years. At this point I am confident there is code in the wild that depends on this being broken.

For this reason I don’t think we can consider it a non-breaking bugfix. It doesn’t solve anything that hasn’t already been broken for two years. So in my view releasing this in a patch or minor (and definitely breaking people) is more disruptive than our usual workflow for breaking changes (add a warning that behavior will change, then change it in the next major).

All of the above assumes we actually want to go with this API. I think this might be a good candidate for an RFC first. It’s not clear to me which alternatives were considered and why they were rejected. The DOM API doesn’t search a string—perhaps there is a good reason for this, and we shouldn’t either?

Copy link
Collaborator

@gaearon gaearon left a comment

Choose a reason for hiding this comment

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

See above

@aweary
Copy link
Contributor

aweary commented Mar 22, 2018

All of the above assumes we actually want to go with this API. I think this might be a good candidate for an RFC first. It’s not clear to me which alternatives were considered and why they were rejected.

The primary hesitation according to #1881 was performance and IE8 (#1881 (comment)). We don't support IE8 and @necolas' benchmarks look good so I think the main question now is whether this is the right API. The only alternative I've seen proposed is using an object like { value: 'blue', important: true}. An RFC sounds good.

I'm 👍 for saving this for a major release.

The DOM API doesn’t search a string—perhaps there is a good reason for this, and we shouldn’t either?

Can you clarify here, what DOM API?

@necolas
Copy link
Contributor Author

necolas commented Mar 22, 2018

I think using an RFC feels like overkill for what is a fix to an old regression. I understand the reasons why it could be considered a breaking change and delayed until React 17, although I'm not sure if the argument is that styling bugs are a special case or that fixing old bugs is always a breaking change (ref #6411). I'll look again at work arounds in RNW or leave it broken and flag the surface where this is an issue for the library. Thanks :)

@gaearon
Copy link
Collaborator

gaearon commented Mar 23, 2018

For #6411, the surface area is vastly smaller IMO. But it would be interesting to first add a warning, and then see how often it fires at FB.

@necolas
Copy link
Contributor Author

necolas commented Mar 23, 2018

#12179 is a patch to add a warning. Would you like me to reopen it or would you prefer to try it internally on www dev?

@necolas
Copy link
Contributor Author

necolas commented Apr 19, 2018

Closing this due to lack of maintainer interest

@necolas necolas closed this Apr 19, 2018
@gaearon
Copy link
Collaborator

gaearon commented Apr 19, 2018

FWIW I don't think we're opposed to supporting this feature per se. It's just that it's not clear a string-based API is the best one here.

@necolas
Copy link
Contributor Author

necolas commented Apr 19, 2018

For such a little feature, designing a new object API seems like it would introduce even more overhead in the areas you are concerned about - DX, bundle size, render time. It's the browser API that's the slow part, and any React API is going to have to use it with the current way styles are applied. And if React doesn't want to support important at all, that's also fine. Either way, I have to look into avoiding important for now.

@gaearon
Copy link
Collaborator

gaearon commented Apr 19, 2018

The part that feels wrong to me is that everybody has to pay for an indexOf lookup that nobody uses today (because the feature doesn't exist) and won't be commonly used in the future.

@sebmarkbage
Copy link
Collaborator

The string search perf angle is valid. It can become significant for many large URLs.

The XSS issue is another one. I don't see an immediate problem but the overlap of SSR and string manipulation is a tough space and might require an even more expensive validation.

@necolas
Copy link
Contributor Author

necolas commented Apr 19, 2018

Yeah I was concerned about that, but there was no indication of an issue in my tests, and you've already introduced a cost like that for custom properties. Everyone would also have to pay a cost if you introduced object values in style. So if any kind of cost in unacceptable you may as well say that this feature will never be supported and add a dev warning to the library. My 2¢

@sebmarkbage
Copy link
Collaborator

Type checking again a value is really fast though typically have to be done regardless at some point in the optimization pipeline (even if this was native/wasm).

I felt like the custom properties thing was motivated by that we probably actually want to change the API to only support the setProperty path which is a major breaking change but more likely to be in line with future direction of CSS such as Typed CSS OM. So eventually we'd get rid of that.

@necolas
Copy link
Contributor Author

necolas commented Apr 19, 2018

IIRC, setProperty is significantly slower than the other (less complete) inline styles API in the browser. Probably why you've been avoiding it unless required, e.g., for custom properties (or !important)

The XSS issue is another one.

👍 hadn't thought about XSS

@sebmarkbage
Copy link
Collaborator

In our measurements setProperty has been significantly faster, which is another reason to do it. Do you have a benchmark in mind? Perhaps it's something we're doing.

@necolas
Copy link
Contributor Author

necolas commented Apr 19, 2018

At first, I looked into only using setProperty for this patch too. That API requires that style properties be converted to the CSS hyphenated form (which is also why I looked at adding memoization to the hyphenation helper you have in fbjs, but it didn't make a noticable difference).

When running the RNW perf regression tests modified to assess this patch, I noticed that using setProperty for all styles (plus converting every property to the hyphenated form) was much slower than the current implementation. Trying it again now (on a 2011 MBP), the time spent in script during rendering of "Mount deep tree" with inline-styles increases from ~43ms to ~66ms (+53%).

@sebmarkbage
Copy link
Collaborator

Ah. The plan was to not convert every property to the hyphenated form but instead require people to specify the hyphenated form in their code. That is the breaking change.

@necolas necolas deleted the necolas/support-important-inline-styles branch March 28, 2019 22:30
Serhey-Oleynik added a commit to Serhey-Oleynik/react-native-app that referenced this pull request Apr 10, 2022
Updates the 'setValueForStyle' implementation to support style values
that contain "!important". This allows the 'flex{Basis,Grow,Shrink}'
values created by the style resolver to be applied. They currently use
the important priority as a work-around for browser-inconsistencies in
the 'flex' shorthand.

Upstream fix: facebook/react#12181

Ref #798
Close #813
superstar1205 added a commit to superstar1205/ReactNative-Web that referenced this pull request Dec 26, 2022
Updates the 'setValueForStyle' implementation to support style values
that contain "!important". This allows the 'flex{Basis,Grow,Shrink}'
values created by the style resolver to be applied. They currently use
the important priority as a work-around for browser-inconsistencies in
the 'flex' shorthand.

Upstream fix: facebook/react#12181

Ref #798
Close #813
crown0128 added a commit to crown0128/react-native-web that referenced this pull request Aug 12, 2023
Updates the 'setValueForStyle' implementation to support style values
that contain "!important". This allows the 'flex{Basis,Grow,Shrink}'
values created by the style resolver to be applied. They currently use
the important priority as a work-around for browser-inconsistencies in
the 'flex' shorthand.

Upstream fix: facebook/react#12181

Ref #798
Close #813
Hiccup19940325 pushed a commit to Hiccup19940325/reactnative_package that referenced this pull request Oct 30, 2023
Updates the 'setValueForStyle' implementation to support style values
that contain "!important". This allows the 'flex{Basis,Grow,Shrink}'
values created by the style resolver to be applied. They currently use
the important priority as a work-around for browser-inconsistencies in
the 'flex' shorthand.

Upstream fix: facebook/react#12181

Ref #798
Close #813
smartDev420 pushed a commit to smartDev420/ReactNative-Web-App that referenced this pull request Aug 2, 2024
Updates the 'setValueForStyle' implementation to support style values
that contain "!important". This allows the 'flex{Basis,Grow,Shrink}'
values created by the style resolver to be applied. They currently use
the important priority as a work-around for browser-inconsistencies in
the 'flex' shorthand.

Upstream fix: facebook/react#12181

Ref #798
Close #813
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants