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

Improve Expensicon lib #1188

Merged
merged 23 commits into from
Jan 26, 2021
Merged

Improve Expensicon lib #1188

merged 23 commits into from
Jan 26, 2021

Conversation

roryabraham
Copy link
Contributor

@roryabraham roryabraham commented Jan 7, 2021

Details

This just improves the scalability of the Expensicon lib

Fixed Issues

https://github.com/Expensify/Expensify/issues/151818

Tests

Just regression test each platform to make sure that .svg assets still work as they should.

Tested On

  • Web
  • Mobile Web
  • Desktop
  • iOS
  • Android

Screenshots

Web

image

Mobile Web

image

Desktop

image

iOS

image

Android

@roryabraham roryabraham self-assigned this Jan 7, 2021
@roryabraham roryabraham requested a review from a team as a code owner January 7, 2021 02:51
@github-actions
Copy link
Contributor

github-actions bot commented Jan 7, 2021

CLA Assistant Lite bot All contributors have signed the CLA ✍️ ✅

@botify botify requested review from nickmurray47 and removed request for a team January 7, 2021 02:52
src/styles/variables.js Outdated Show resolved Hide resolved
@roryabraham
Copy link
Contributor Author

Okay, this is ready for another round of review. I separated out brand assets from Expensicons.

nickmurray47
nickmurray47 previously approved these changes Jan 7, 2021
Copy link
Contributor

@nickmurray47 nickmurray47 left a comment

Choose a reason for hiding this comment

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

LGTM and tests well

Copy link
Contributor

@marcaaron marcaaron left a comment

Choose a reason for hiding this comment

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

Looks great overall. Just have a few style notes and minor ideas.

src/components/Icon/BRAND_ASSETS.js Outdated Show resolved Hide resolved
src/components/Icon/EXPENSICONS.js Outdated Show resolved Hide resolved
src/components/Icon/index.js Outdated Show resolved Hide resolved
const ICONS = _.extend(BRAND_ASSETS, EXPENSICONS);

const propTypes = {
icon: PropTypes.oneOf(_.values(ICONS)).isRequired,
Copy link
Contributor

Choose a reason for hiding this comment

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

I appreciate this but I think this could just be validated that it's some kind of component rather than need to validate that it's one of the large list of icon components and it would simplify this a bit.

src/components/Icon/index.js Outdated Show resolved Hide resolved
src/components/Icon/index.js Outdated Show resolved Hide resolved
src/components/Icon/index.js Show resolved Hide resolved
Icon.propTypes = propTypes;
Icon.defaultProps = defaultProps;

export default memo(Icon);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there any reason we might want to prevent this component from re-rendering?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The purpose for memoizing the component was to boost performance. Icons will render often (and almost always with the same props), so this seems like the classic use-case for React.memo.

Copy link
Contributor

Choose a reason for hiding this comment

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

That sounds reasonable to me. I just have a few questions...

Icons will render often

I'm wondering if this will always be the case? Maybe some icons will not render often? And even if they did... is it expensive to render an SVG? Said another way, is there some specific performance issue that we are trying to prevent? Should we just wrap all the things in React.memo() in that case? Is there any reason why we might want to use React.memo() judiciously? Or is it no big deal?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Here's an article that provides some pretty good insight on when it is and is not a good idea to use React.memo. This diagram summarizes the heuristic pretty well:

image

As far as I understand, it's a balancing act between the time it takes to compare the props for equality vs the time it takes for the component to render. I think if you had a component with no props (unlikely, but possible), then you would always want to use React.memo, because the equality comparison is close to instantaneous.

The Icon component for sure meets the first three criteria in the diagram above, and it's a bit of a toss-up whether or not it meets the third. I have no idea how to measure and know for sure. Probably not a significant difference either way, but maybe @tgolen would have a good 6th sense as to whether or not this is a good time to use React.memo.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for sharing this. I read the article and the impression I have goes something like...

If the time it takes to perform a React.memo() shallow comparison is greater than the time it takes to render a component then we might actually be slowing things down by using React.memo()?

The author does mention to use profiling tools to measure the benefits which sounds like a smart idea, but I might not pull them out in the first place unless something seemed slow.

So, I guess I'm sort of in the camp that we should use memo, PureComponent, etc when it is very clear why they are being used, there is some measurable benefit, and if we do not use them performance will degrade in an obvious way.

That said, if there is no measurable benefit in the other direction and the component still does what it's supposed to maybe it doesn't matter? At least one person in this SO post said you should "always" use it so I really don't know.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also whatever we decide we should turn this into a SO post bc this is a good chat.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, agreed. I want to wait to see what Tim G has to say about it.

@shawnborton
Copy link
Contributor

Just to double check here, are have you converted all of the icons into svgs here? I don't think so but wanted to check.

I think the outstanding list is something like:

  • pencil icon for draft messages
  • attach icon
  • send/paper plane icon
  • close (x) icon

I'm happy to provide all of those assets for you in .svg format if you want to get them into this PR.

@tgolen
Copy link
Contributor

tgolen commented Jan 8, 2021

Before jumping into the code in this PR, I'd like to take a step back because I think this is missing a problem statement. What is the problem being solved here? Has it been documented and confirmed? Is it enough of a problem that it requires refactoring?

Understanding that will help me to go into the code with the right context, but at this point, I am not sure why these changes are necessary.

@roryabraham
Copy link
Contributor Author

@tgolen So high-level the problem solved by this PR is something like this:

In its current form, the Expensicon library requires that each icon be its own component, despite the fact that every component will have nearly the same props and implementation. This creates a scalability issue, where adding a new icon involves creating a duplicate component unnecessarily.

This PR solves that problem by creating a reusable component for icons of different types. Icon assets are collected into their own file, so adding a new icon is as simple as adding the asset to assets/images, then adding it to the list of icons.

@roryabraham
Copy link
Contributor Author

@shawnborton happy to include all the other assets too - should be pretty simple

@shawnborton
Copy link
Contributor

Here ya go: IconsForRory.zip

Copy link
Contributor

@Julesssss Julesssss left a comment

Choose a reason for hiding this comment

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

SVG and reusable component refactor ❤️

Few minor points to discuss

Julesssss
Julesssss previously approved these changes Jan 22, 2021
@Julesssss
Copy link
Contributor

Any final comments @shawnborton ?

src/components/Icon/BrandAssets.js Outdated Show resolved Hide resolved
src/components/Icon/index.js Outdated Show resolved Hide resolved
src/components/Icon/index.js Outdated Show resolved Hide resolved
src/components/Icon/index.js Outdated Show resolved Hide resolved
shawnborton
shawnborton previously approved these changes Jan 22, 2021
shawnborton
shawnborton previously approved these changes Jan 25, 2021
Julesssss
Julesssss previously approved these changes Jan 25, 2021
@roryabraham
Copy link
Contributor Author

Okay, simplified and updated to only deal with Expensicons and not brand assets for now.

@marcaaron marcaaron merged commit 6424cfd into master Jan 26, 2021
@marcaaron marcaaron deleted the Rory-ImproveExpensicons branch January 26, 2021 18:45
@github-actions github-actions bot locked and limited conversation to collaborators Jan 26, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants