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

Animations: Unnecessary re-renders caused by WAAPIWrapper #9742

Closed
swissspidy opened this issue Nov 18, 2021 · 3 comments · Fixed by #10024
Closed

Animations: Unnecessary re-renders caused by WAAPIWrapper #9742

swissspidy opened this issue Nov 18, 2021 · 3 comments · Fixed by #10024
Assignees
Labels
Group: Animations Group: Workspace A group encompassing all of workspace – everything inside the canvas and sidebar Type: Bug Something isn't working Type: Performance Performance related issues and enhancements.

Comments

@swissspidy
Copy link
Collaborator

Bug Description

This finding was kindly shared with us by rtCamp engineers:

Issue: Any element wrapped by StoryAnimation.WAAPIWrapper / StoryAnimation.Provider get's re-rendered whenever an element gets moved to another place. This also causes videos or images to be loaded again due to the re-renders, resulting in tons of HTTP requests.

Cause: currently, all the animation properties of display elements are being stored in a Map, so whenever the (x, y) position of an element changes, that Map gets updated and all of those elements depending on animations get re-rendered.

Relevant code:

https://github.com/google/web-stories-wp/blob/31776678fb986ad1f11461ca9febf900ec4f7c3d/packages/animation/src/components/provider.js#L81-L118

Expected Behaviour

No unexpected re-renders

Steps to Reproduce

Check renders in React dev tools, see also video below showing the HTTP requests

Screenshots

https://share.vidyard.com/watch/PTXDn2R1L7XqPyX57Y3tg4?

Additional Context

  • Plugin Version: 1.14.0
  • WordPress Version: 5.8
  • Operating System: macOS
  • Browser: Chrome
@swissspidy swissspidy added Type: Bug Something isn't working Group: Animations Type: Performance Performance related issues and enhancements. Pod: Pea Group: Workspace A group encompassing all of workspace – everything inside the canvas and sidebar labels Nov 18, 2021
@maxyinger
Copy link
Contributor

Ahh great find. I'll look into this

@maxyinger
Copy link
Contributor

maxyinger commented Nov 19, 2021

Alright, so did a little recon on this today to try and figure out some possible solutions. Want to do a little brain dump so we don't lose any context while I'm out on vacation.

React's diffing algorithm with our animation patterns, look to be the primary culprit. Basically if the parent is different, react will destroy the old tree and build up a new one (we're essentially opting out of one of the heuristics react relies on with this pattern):
https://reactjs.org/docs/reconciliation.html#elements-of-different-types
https://github.com/google/web-stories-wp/blob/e0af2f160f4141e77179253b5d10b38b3c30735f/packages/animation/src/components/WAAPIWrapper.js#L28-L51

The WAAPI implementation used to only hold pure data in provider state and not actually hold components. I believe we originally altered it to hold components just to mirror our AMP implementation.

I see no reason why we can't revert the underlying generated WAAPI animations to just be pure data and keep all the markup in our WAAPIWrapper component. I believe we can then structure this in a way where react doesn't regenerate underlying DOM nodes on every animation update, but instead just updates the props.

To update the provider state to only hold pure data for WAAPI animations, we'll need to go through our animation parts and effects to essentially only return these attributes for the WAAPIAnimation property:

   {
      timings,
      keyframes,
      useClippingContainer,
      targetLeafElement,
    }

Most of this should be taken care of by simply updating simpleAnimation.js, however, there are some animation effects that compose their animations by using nested react components ie flyIn, rotateIn, etc. For these we'll need to go through and recompose the animation through keyframes (kind of like how we do in backgroundPanAndZoom)

Lastly we'll need to update WAAPIWrapper to expect WAAPIAnimation as pure data and render markup accordingly that doesn't violate the react diffing heuristics.

Alternative Approaches
Originally I was trying to think of a way to opt out of react recreating a new tree of components when the rendered children hasn't changed. Seems to be a common problem with things like drag n drop libraries where they may move an expensive component to a new container, and don't want react to regenerate that expensive component when nothing has changed but its parent.

The term for this scenario is called reparenting :
facebook/react#3965

This thread has two propositions to get around this.

Conclusion
Ultimately I think it's better to just go back and fix the underlying patterns to be more compatible with reacts current reconciliation algorithm vs trying to further opt out of them because we're doing something antithetical to idiomatic react.

@bmattb bmattb added this to the Sprint 66 - Dec 6 to 17 - the missing sprint milestone Dec 20, 2021
@felipebochehin87
Copy link

Tested against https://stories-qa-wordpress-amp.pantheonsite.io/wp-admin, using PR #10024.

Added videos to the canvas and moved them, changing their position. No new requests found on Network > Media tab.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Group: Animations Group: Workspace A group encompassing all of workspace – everything inside the canvas and sidebar Type: Bug Something isn't working Type: Performance Performance related issues and enhancements.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants