Skip to content

Conversation

jcfilben
Copy link
Collaborator

@jcfilben jcfilben commented Sep 4, 2024

What does this PR do?

Remove attrs and instead pass through the theme whe we are outside of a ThemeContext via props.

Where should the reviewer start?

What testing has been done on this PR?

Performance on COM with this branch
Screenshot 2024-09-04 at 10 53 23 AM

Screenshot 2024-09-04 at 10 54 08 AM

Performance on COm with grommet 2.38.0 (pre attrs)
image

Performance on COM with grommet 2.39.0 (attrs)
image

How should this be manually tested?

Do Jest tests follow these best practices?

  • screen is used for querying.
  • The correct query is used. (Refer to this list of queries)
  • asFragment() is used for snapshot testing.

Any background context you want to provide?

What are the relevant issues?

Closes grommet/hpe-design-system#4135

Screenshots (if appropriate)

Do the grommet docs need to be updated?

no

Should this PR be mentioned in the release notes?

yes

Is this change backwards compatible or is it a breaking change?

backwards compatible

@jcfilben jcfilben requested review from taysea and britt6612 September 4, 2024 17:03
@taysea
Copy link
Collaborator

taysea commented Sep 4, 2024

Just to summarize for my own understanding --

If we are using v2.38.0 as a baseline of 587ms:

  • This approach averages to ~500ms -- comparable to the performance in v2.38.0
  • The v2.39.0 was >1000ms

If that's correct, this looks like a good solution.

Copy link
Collaborator

@taysea taysea left a comment

Choose a reason for hiding this comment

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

Looks good to me!

One idea for ease of changes in future would be to abstract some of the logic into a custom hook like we have for useThemeValue. Not sure if this logic is perfect, but just a concept-- and not sure if there's any performance downsides that come with this approach or not.

// in component files
const themeContext = useThemeContext(theme);

<Component
   {...themeContext}
// in utils files

const useThemeContext = (theme) => {
   const existingThemeContext = useContext(ThemeContext);
   return existingThemeContext === undefined ? { theme } : {}
}

That said, the work is complete on the PR and functionality seems to be working as expected, so soft opinion if we do any cleanups or just keep it as it is.

@jcfilben
Copy link
Collaborator Author

jcfilben commented Sep 4, 2024

One idea for ease of changes in future would be to abstract some of the logic into a custom hook like we have for useThemeValue. Not sure if this logic is perfect, but just a concept-- and not sure if there's any performance downsides that come with this approach or not.

// in component files
const themeContext = useThemeContext(theme);

<Component
{...themeContext}
// in utils files

const useThemeContext = (theme) => {
const existingThemeContext = useContext(ThemeContext);
return existingThemeContext === undefined ? { theme } : {}
}
That said, the work is complete on the PR and functionality seems to be working as expected, so soft opinion if we do any cleanups or just keep it as it is.

Good idea, I agree this would be cleaner. For now I left as is, maybe we can just try and get this update out and then handle this step in a separate PR?

@jcfilben
Copy link
Collaborator Author

jcfilben commented Sep 4, 2024

Just to summarize for my own understanding --

If we are using v2.38.0 as a baseline of 587ms:

This approach averages to ~500ms -- comparable to the performance in v2.38.0
The v2.39.0 was >1000ms
If that's correct, this looks like a good solution.

I think it is also helpful to looks at the full timeline,
v2.38.0 and this branch is ~1500ms
v2.39.0 is ~4500ms

@@ -105,6 +107,7 @@ const Anchor = forwardRef(
if (onBlur) onBlur(event);
}}
size={sizeProp || size}
{...(withinThemeContext === undefined ? { theme } : {})}
Copy link
Collaborator

Choose a reason for hiding this comment

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

This pattern is repeated way too much and is awkward so it's intent is hard to understand.

Also, if a theme prop is passed in, doesn't it end up in rest and get overwritten by the theme from the context if there is one?

I think a pattern of something like:

const theme = useTheme(themeProp);

<StyledAnchor theme={theme} ... />

Is much cleaner assuming the useTheme does the useContext(ThemeContext) and logic to reconcile the theme prop from the component and theme from the theme context if needed.

This also could be done to prevent the theme parameter from changing each render (your ternary create two new objects each render)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Also, if a theme prop is passed in, doesn't it end up in rest and get overwritten by the theme from the context if there is one?

I tried to keep {...(withinThemeContext === undefined ? { theme } : {})} above {...rest} so that rest would override the theme, looks like I actually missed this on Anchor though.

I think a pattern of something like:

const theme = useTheme(themeProp);

<StyledAnchor theme={theme} ... />

Is much cleaner assuming the useTheme does the useContext(ThemeContext) and logic to reconcile the theme prop from the component and theme from the theme context if needed.

I wanted to avoid having an explicit themeProp on each component and passing through the theme on each styled component if we don't need it (if we are within a theme context already). I'm not sure performance wise if it matters or not if we pass a large object (the theme) as a prop for each styled component regardless of whether we are within a theme context or not.

This also could be done to prevent the theme parameter from changing each render (your ternary create two new objects each render)

This is a good point, I hadn't thought about that cost

I'll add that performance is an area I'm not super familiar with so definitely open to other perspectives and open to going this route, just wanted to share my thought process of why I approached this the way that I did.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not sure what you're concerned with as far as performance of passing a theme prop. It seems like you're already doing that in most of the cases via the value from useThemeValue and passing it to the styled component (like Anchor here.) I would think in most cases you'd get the benefit with no real cost if useThemeValue() accepted a themeProp arg and did the logic discussed. Components that use useThemeValue and others could easily just check their props for the theme prop and pass it along. I don't think passing a large object is expensive (ie. it's a reference unless you're doing something expensive like a merge.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ok sounds good, I'll work on making this change

Copy link
Collaborator Author

@jcfilben jcfilben Sep 5, 2024

Choose a reason for hiding this comment

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

I implemented this change but I'm seeing worse performance on the com repo compared to this branch as it currently is. Maybe if someone is willing they could run on their end as well just to validate that they are getting the similar results as I am. I'll push up the changes in a new branch so it is easier to compare.

Here is what I'm seeing with this approach:
Com sample 1 with alt branch (#7306)
Screenshot 2024-09-05 at 10 32 13 AM
Com sample 2 with alt branch (#7306)
Screenshot 2024-09-05 at 10 32 31 AM
Com sample 3 with alt branch (#7306)
Screenshot 2024-09-05 at 11 02 59 AM

Copy link
Collaborator Author

@jcfilben jcfilben Sep 5, 2024

Choose a reason for hiding this comment

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

I just tested this current branch again to see if me being in a different location on different internet was causing a problem, it looks like it is not. Here is what I got
Com sample 1 with this branch:
Screenshot 2024-09-05 at 11 19 01 AM
Com sample 2 with this branch
Screenshot 2024-09-05 at 11 20 09 AM

Copy link
Collaborator Author

@jcfilben jcfilben Sep 5, 2024

Choose a reason for hiding this comment

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

To summarize the results that I am getting:

This branch with withinThemeContext approach = ~1500ms

Alternate branch #7306 with themeProp approach = ~2500ms

Again would be good to have someone else validate these results just to make sure we are seeing the same thing

Copy link
Collaborator

Choose a reason for hiding this comment

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

I just tested this current branch again to see if me being in a different location on different internet was causing a problem, it looks like it is not. Here is what I got Screenshot 2024-09-05 at 11 19 01 AM Screenshot 2024-09-05 at 11 20 09 AM

Can you please label these images with product + approach/version so that we know what we are comparing? Also, we need the equivalent for v2.38 so that we have our baseline.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah I'll add more detailed labels, baseline can be found in the PR description

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Com, grommet 2.38.0 tested at new location
Screenshot 2024-09-05 at 1 49 00 PM

@taysea
Copy link
Collaborator

taysea commented Sep 6, 2024

v2.38.0 (forgot to grab screenshots)

  • Run 1: 730.6000000238419
  • Run 2: 765.7000000476837
  • Run 3: 781.9000000357628
  • Run 4: 763
  • Run 5: 792.0999999642372

Mean - 766

PR #7304 (This PR)

  • Run 1: 756.4000000357628
  • Run 2: 655.7000000476837
  • Run 3: 682.6999999880791
  • Run 4: 676.5
  • Run 5: 745.5

Mean - 703.3

Screenshot 2024-09-06 at 10 11 55 AM Screenshot 2024-09-06 at 10 12 12 AM Screenshot 2024-09-06 at 10 12 22 AM Screenshot 2024-09-06 at 10 12 39 AM Screenshot 2024-09-06 at 10 12 55 AM

PR #7306

  • Run 1: 660.3999999761581
  • Run 2: 639.8999999761581
  • Run 3: 668.3000000119209
  • Run 4: 674
  • Run 5: 651

Mean - 658.7

Screenshot 2024-09-06 at 10 29 36 AM Screenshot 2024-09-06 at 10 29 45 AM Screenshot 2024-09-06 at 10 29 57 AM Screenshot 2024-09-06 at 10 30 10 AM Screenshot 2024-09-06 at 10 30 22 AM

Takeaways

Both PR branches performed comparably to v2.38.0 and better than v2.39.0.

@jcfilben
Copy link
Collaborator Author

jcfilben commented Sep 6, 2024

I'm back at home today and I tested again and now I'm getting similar results to Taylor. I can publish my results soon. Yesterday my internet was definitely slower. I wonder if the speed discrepancy between approaches I was seeing is only apparent on slower internet speeds? Or maybe my results yesterday were a fluke, not sure.

@jcfilben
Copy link
Collaborator Author

jcfilben commented Sep 6, 2024

v2.38.0

coming soon...

PR #7304 (This PR)

  • Run 1: 1713.6000001430511
  • Run 2: 1716.2000000476837
  • Run 3: 1704.2000000476837
  • Run 4: 1693.7999999523163
  • Run 5: 1706.0999999046326

Mean 1706.4

Screenshot 2024-09-06 at 12 29 15 PM Screenshot 2024-09-06 at 12 29 30 PM Screenshot 2024-09-06 at 12 30 05 PM Screenshot 2024-09-06 at 12 55 11 PM Screenshot 2024-09-06 at 12 55 24 PM

PR #7306

  • Run 1: 1789.0999999046326
  • Run 2: 1627.4000000953674
  • Run 3: 1661.8999998569489
  • Run 4: 1621.8999998569489
  • Run 5: 1681.0999999046326

Mean - 1675.8

Screenshot 2024-09-06 at 12 09 45 PM Screenshot 2024-09-06 at 12 10 19 PM Screenshot 2024-09-06 at 12 10 39 PM Screenshot 2024-09-06 at 12 10 54 PM Screenshot 2024-09-06 at 12 45 41 PM

@jcfilben
Copy link
Collaborator Author

jcfilben commented Sep 9, 2024

Update 9/9/24 I chatted with the rest of the dev team, some notes from that discussion:

ThemeContext.Extend behavior outside of Grommet

In v2.38.0 and before if you have a component outside of Grommet but within ThemeContext.Extend it will not get the base theme props. In v2.39.0 when we remove defaultProps and switched to attrs this behavior changed and components outside of Grommet but within ThemeContext.Extend will receive the default theme props. This can be seen in this codesandbox by changing the Grommet dependency version between 2.38.0 and 2.39.0. https://codesandbox.io/p/sandbox/grommet-v2-template-forked-s5h2jv?file=%2Findex.js%3A15%2C74.

We discussed what the expected behavior should be and decided that we want to go back to the behavior we were seeing in 2.38.0 and before, where the ThemeContext.Extend outside of Grommet does not receive the base theme props. We discussed that the change in behavior in 2.39.0 was not intentional and just an unexpected side effect of switching to attrs. We also felt that having the base theme props come through in this scenario was a bit unexpected. Lastly we also mentioned that this feels like an unlikely scenario for users to run into. It seems like people are using the Grommet wrapper and therefore are unlikely to run into this. We haven't had issues filed or questions raised in slack around theming outside of the Grommet wrapper.

PR #7304 (This PR) or PR #7306?

We decided to move forward with this PR (#7304) and clean up some of the logic so that it is easier to read and less repetitive.

The main reason we did not go with the alternate approach in #7306 was we didn't want to add a theme prop to all the Grommet components. Adding a theme prop felt heavy and went against the idea that the theme is global rather than being controlled potentially at the component instance level.

@jcfilben jcfilben requested review from taysea and halocline September 9, 2024 21:47
@jcfilben
Copy link
Collaborator Author

jcfilben commented Sep 10, 2024

Performance snapshots on com after 'simplify code' commit 9/10/24

  • Run 1: 2559.600000143051
  • Run 2: 2400.0999999046326
  • Run 3: 2383.899999856949
  • Run 4: 2789.800000190735
  • Run 5: 2616.7999999523163

Mean - 2550.0

Screenshot 2024-09-10 at 9 33 03 AM Screenshot 2024-09-10 at 9 33 27 AM Screenshot 2024-09-10 at 9 33 57 AM Screenshot 2024-09-10 at 9 34 17 AM Screenshot 2024-09-10 at 9 34 32 AM

2.38.0 snapshots on com 9/10/24

  • Run 1: 2439.800000190735
  • Run 2: 3067.7999999523163
  • Run 3: 2515.2999999523163
  • Run 4: 2958.2999999523163
  • Run 5: 2951.5

Mean - 2786.5

Screenshot 2024-09-10 at 9 43 12 AM Screenshot 2024-09-10 at 9 43 39 AM Screenshot 2024-09-10 at 9 43 58 AM Screenshot 2024-09-10 at 9 44 21 AM Screenshot 2024-09-10 at 9 44 39 AM

Copy link
Collaborator

@britt6612 britt6612 left a comment

Choose a reason for hiding this comment

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

Nice clean up just small comments/questions

.withConfig(styledComponentsConfig)
.attrs((props) => ({
...withTheme(props),
'aria-label': props.a11yTitleProp,
Copy link
Collaborator

Choose a reason for hiding this comment

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

do we need to add this or its fine being removed? not sure why it was pulled out but we no longer have aria-label in this file just making sure that is intentional?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This change is intentional, in the Grid.js file I renamed a11yTitleProp to aria-label so that it will pass through correctly without needing 'aria-label': props.a11yTitleProp in attrs.

Copy link
Collaborator

@britt6612 britt6612 left a comment

Choose a reason for hiding this comment

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

Thanks for making these changes

Copy link
Collaborator

@taysea taysea left a comment

Choose a reason for hiding this comment

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

one small comment, otherwise looks good

@taysea taysea self-requested a review September 10, 2024 20:17
Copy link
Collaborator

@taysea taysea left a comment

Choose a reason for hiding this comment

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

LGTM.

@jcfilben jcfilben merged commit 5c8eb4b into grommet:master Sep 10, 2024
13 of 14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Grommet - styled components attrs performance
5 participants