Skip to content

[Emotion] Memoize EuiFlex and EuiSpacer component styles#7582

Merged
cee-chen merged 6 commits intoelastic:mainfrom
cee-chen:emotion/memoize-flex-spacer
Mar 15, 2024
Merged

[Emotion] Memoize EuiFlex and EuiSpacer component styles#7582
cee-chen merged 6 commits intoelastic:mainfrom
cee-chen:emotion/memoize-flex-spacer

Conversation

@cee-chen
Copy link
Copy Markdown
Contributor

@cee-chen cee-chen commented Mar 14, 2024

Summary

This PR is an Emotion performance pass of EuiSpacer, EuiFlexItem, EuiFlexGroup (and EuiFlexGrid while 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

  • Browser QA
    • Checked in both light and dark modes
    • Checked in mobile
    • Checked in Chrome, Safari, Edge, and Firefox
      - [ ] Checked for accessibility including keyboard-only and screenreader modes
  • Docs site QA - N/A
  • Code quality checklist
  • Release checklist - N/A, skipping as this shouldn't affect end-users or consumers
  • Designer checklist - N/A

@cee-chen cee-chen added tech debt emotion performance skip-changelog Use on PRs to skip changelog requirement (Don't delete - used for automation) labels Mar 14, 2024
@cee-chen cee-chen marked this pull request as ready for review March 14, 2024 04:26
@cee-chen cee-chen requested a review from a team as a code owner March 14, 2024 04:26
@kibanamachine
Copy link
Copy Markdown

Preview staging links for this PR:

@elasticmachine
Copy link
Copy Markdown
Collaborator

💚 Build Succeeded

`,
direction: {
row: css``,
// Note: the only way to get column direction working with `display: grid`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Imho definitely a reasonable usage of inline style for ensuring to be able to memoize the rest of the styles 👍

@tkajtoch
Copy link
Copy Markdown
Member

I tested the performance difference locally and in the PR environment. The difference is massive on pages using EuiFlex a lot, like the Icons documentation page you suggested. I measured it completing the first render 500ms faster on average, which results in about 50% gain. In regular views, the difference would obviously be way smaller but still measurable!

There's no difference when switching themes, though, because EuiFlexItem doesn't use EuiThemeContext and doesn't rerender.

column: css`
grid-auto-flow: column;
grid-template-rows: repeat(${gridTemplateRows}, 1fr);
/* grid-template-rows set via inline style */
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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. 🤔

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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(() => {
Copy link
Copy Markdown
Member

@tkajtoch tkajtoch Mar 14, 2024

Choose a reason for hiding this comment

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

[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.

Copy link
Copy Markdown
Contributor Author

@cee-chen cee-chen Mar 15, 2024

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Member

@tkajtoch tkajtoch left a comment

Choose a reason for hiding this comment

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

The changes look and work great!

@cee-chen
Copy link
Copy Markdown
Contributor Author

There's no difference when switching themes, though, because EuiFlexItem doesn't use EuiThemeContext and doesn't rerender.

Ooh that makes total sense 🤦 Thanks a million for explaining Tomasz! Super happy that there's a noticeable impact on performance!

@cee-chen cee-chen merged commit bb132ea into elastic:main Mar 15, 2024
@cee-chen cee-chen deleted the emotion/memoize-flex-spacer branch March 15, 2024 00:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

emotion performance skip-changelog Use on PRs to skip changelog requirement (Don't delete - used for automation) tech debt

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants