Skip to content

Conversation

@kitten
Copy link
Contributor

@kitten kitten commented Aug 1, 2019

Fix motiondivision/motion#256

This bug was discovered by running react-ssr-prepass over the motion codebase; See: https://github.com/framer/motion/blob/b0dcfa8/src/motion/utils/use-motion-values.ts#L175

The bug can be reproduced with any component nested in a forwardRef component then nested in a memo higher-order component:

memo(forwardRef(SomeComponent))

The visitor for memo components erroneously assumes that the nested type can only be a class or function component. It entirely ignores other types of "special" components like a forwardRef component.

The fix is to remove this special path and instead return an element from the memo component's nested type. This will correctly return a forwardRef element.
component visitor.

In the case of: memo(forwardRef(SomeComponent)) The memo component would be correctly detected but would assume that it may only contain a class or function component and render it. Instead of rendering the nested component, this fix returns a proper element from the memo component visitor.
@kitten kitten added the bug Something isn't working label Aug 1, 2019
@codecov-io
Copy link

Codecov Report

Merging #15 into master will decrease coverage by <.01%.
The diff coverage is 100%.

Impacted file tree graph

@@ Coverage Diff @@ ## master #15 +/- ## ========================================= - Coverage 97.31% 97.3% -0.01%  ========================================= Files 10 10 Lines 484 483 -1 Branches 87 87 ========================================= - Hits 471 470 -1  Misses 9 9 Partials 4 4
Impacted Files Coverage Δ
src/visitor.js 100% <100%> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 6e69818...dc857ae. Read the comment docs.

const memoElement = ((element: any): MemoElement)
const type = memoElement.type.type
const fauxElement = (createElement((type: any), memoElement.props): any)
const child = render(type, memoElement.props, queue, visitor, fauxElement)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is obviously a very small change, but to leave a comprehensive explanation in this PR:
The fauxElement was creating a the React.Element correctly but was assuming it can only be an element of a function or class component, hence it immediately jumps into render. The alternative is to just return the React.Element, not assume anything about it, and let the visitElement visitor run over it again to check what kind of element it really is

@kitten kitten merged commit 5cf61c9 into master Aug 1, 2019
@kitten kitten deleted the fix/forward-ref-in-memo branch August 1, 2019 11:05
kitten added a commit that referenced this pull request Jan 17, 2020
This is the same fix as #15, but for the opposite way around.
kitten added a commit that referenced this pull request Jan 17, 2020
This is the same fix as #15, but for the opposite way around.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

4 participants