[Emotion] Memoize EuiFlex and EuiSpacer component styles#7582
[Emotion] Memoize EuiFlex and EuiSpacer component styles#7582cee-chen merged 6 commits intoelastic:mainfrom
EuiFlex and EuiSpacer component styles#7582Conversation
- can just be a static obj
`useEffect` change causes issues in react 18, but IMO the production perf gain is worth the test tradeoff
- requires inlining styles for `direction="column"`
|
Preview staging links for this PR:
|
💚 Build Succeeded
|
| `, | ||
| direction: { | ||
| row: css``, | ||
| // Note: the only way to get column direction working with `display: grid` |
There was a problem hiding this comment.
Imho definitely a reasonable usage of inline style for ensuring to be able to memoize the rest of the styles 👍
|
I tested the performance difference locally and in the PR environment. The difference is massive on pages using There's no difference when switching themes, though, because |
| column: css` | ||
| grid-auto-flow: column; | ||
| grid-template-rows: repeat(${gridTemplateRows}, 1fr); | ||
| /* grid-template-rows set via inline style */ |
There was a problem hiding this comment.
[not a change request]
You could also extract only this CSS to a separate generator function (or even include it directly) and not memoize it :)
There is a slight performance penalty for using the style attribute. React iterates over the style object keys and parses them to a style string which is then parsed as inline CSS by the browser and applied to the element. Compared to a regular class attribute, it's a few times slower to process, but considering Emotion's css also takes time to run, there shouldn't be much difference.
There was a problem hiding this comment.
Wouldn't the performance penalty with Emotion having to re-generate a brand new className on the fly offset that? I'm genuinely curious as personally in EUI we recommend using inline styles for any CSS that is dynamic / can change based on user input (e.g. colors, in this case the row # is derived from content so could also be dynamic). If that's not actually more performant, we'll need to both rewrite our guidelines and refactor several styles in EUI. 🤔
There was a problem hiding this comment.
I'm going to go ahead and and merge this PR so it gets in for next Monday's release, but I definitely want to continue this conversation!
| `${component} is not a valid element type. Use \`div\` or \`span\`.` | ||
| ); | ||
| } | ||
| useEffect(() => { |
There was a problem hiding this comment.
[not a change request, just sharing my thoughts]
I'm not a huge fan of checks like these since they don't add much value. Technically we could allow any tag name here and limit it in TypeScript only to 'div' | 'span', keyof React.JSX.IntrinsicElements or just string (so custom web components are allowed, for example).
In case this limitation is necessary, I like to put it inside something like this:
if (process.env.NODE_ENV !== "production") {
if (!COMPONENT_TYPES.includes(component)) {
throw new Error(`${component} is not a valid element type. Use \`div\` or \`span\`.`)
}
}so it can be tree shaken from production builds and save a little bit of compute time and bundle size.
There was a problem hiding this comment.
Yeah I'd be fine tossing the check personally. I'm guessing it was written years before the codebase was in Typescript, but even then I frankly don't see why it matters that we limit users to a certain element type - why not allow devs to put flex styles on a <form> or a <section> for example? 🤷 Why would the element type matter in this case?
I was mostly just trying to maintain parity / not cause a changelog item in the memoization work, but personally, I'd be super down to open a follow-up PR next week that removes these thrown errors completely.
tkajtoch
left a comment
There was a problem hiding this comment.
The changes look and work great!
Ooh that makes total sense 🤦 Thanks a million for explaining Tomasz! Super happy that there's a noticeable impact on performance! |
Summary
This PR is an Emotion performance pass of
EuiSpacer,EuiFlexItem,EuiFlexGroup(andEuiFlexGridwhile we're here). These components have some of the highest usage across Kibana, so fingers crossed that we get some solid performance gains from this 🤞It also incidentally does some test cleanup while here and adds some very minor
useEffect()s (to only run certain prop checks on that prop change and not on every rerender). As always, I recommend code reviewing by commit.QA
No UI should have changed/regressed as part of this PR.
General checklist
- [ ] Checked for accessibility including keyboard-only and screenreader modesAdded orupdated jestand cypress tests