Skip to content

[core] forward innerRef for certain components#14536

Merged
oliviertassinari merged 35 commits into
mui:nextfrom
eps1lon:feat/forwar-ref
Mar 1, 2019
Merged

[core] forward innerRef for certain components#14536
oliviertassinari merged 35 commits into
mui:nextfrom
eps1lon:feat/forwar-ref

Conversation

@eps1lon

@eps1lon eps1lon commented Feb 14, 2019

Copy link
Copy Markdown
Member

It's recommended to review one commit at a time.

Two reasons I already open this:

  • I wanted to see what Draft Pull Request offers
  • I consider the individual commits itself complete. If anybody has some spare time early review would be more than welcome.

Breaking

Wraps components in forwardRef. Since every component is wrapped in withStyles this will only affect the innerRef prop. They will no longer return a ref to the instance (or error for function components) but rather the underlying DOM node where useful1.

Some components are not included (yet). Those are the ones where virtually no test was passing so I defer them to a separate PR:

The initial change broke about 25% of our unit tests. Some of them were due to own mistakes, some because forwardRef was not appropriate but overall I suspect 15% of the breakage was due to flawed tests. I think https://blog.kentcdodds.com/why-i-never-use-shallow-rendering-c08851a68bb7 is a pretty good starting point to explain why.

Implementation details

  • function components are simply wrapped in their original function. I may rename the component var names later because const Foo = React.forwardRef(function Foo() {}) is quite tricky WRT to what Foo actually references
  • class components are wrapped in a withForwardRef HOC that injects the ref from forwardRef into innerRef. Since that prop is intercepted by withStyles it does not cause any property collision.

Notes for reviewers

Looking through one function and one class component implementation is sufficient. The rest follow the same pattern. The critical parts are the test files.

1 This excludes TouchRipple. We need imperative handles here (see useImperativeHandle to trigger pulsating).

@eps1lon eps1lon added core breaking change Introduces changes that are not backward compatible. labels Feb 14, 2019
@eps1lon eps1lon added this to the v4 milestone Feb 14, 2019
@oliviertassinari

Copy link
Copy Markdown
Member

Could we run the CI?

@eps1lon

eps1lon commented Feb 14, 2019

Copy link
Copy Markdown
Member Author

Could we run the CI?

Sure. But it's far from ready yet and I didn't want to waste resources.

@oliviertassinari

oliviertassinari commented Feb 14, 2019

Copy link
Copy Markdown
Member

Sure. But it's far from ready yet and I didn't want to waste resources.

@eps1lon Thanks, I was interested in the bundle size.

My biggest issue is how we gonna educate people to correctly apply the ref when implementing custom components. 90% of the libraries of the community doesn't correctly forward the reference. I have just tested react-router. I can't get a ref on the DOM an element when using the Link. They don't seem to be interested in solving the problem: remix-run/react-router#6195. Same push back from reactjs/react-transition-group#457. When I all the changes needed, maybe the best move it to "wait and see" 🤔 but it has almost been 1 year since: #10825 (comment).

@eps1lon

eps1lon commented Feb 14, 2019

Copy link
Copy Markdown
Member Author

You can patch react-router-dom by wrapping it in another component: https://github.com/mui-org/material-ui/pull/13664/files#diff-52b99e4a854fd9a4fc16ebc3353cd14dR131

This approach should work with most libraries. As far as I know most of the libraries have some sort of innerRef prop.

Come to think of it it might be possible to check if a component allows refs to the DOM node (forwardRef or string elementType) and use ref in those cases, otherwise assume innerRef works. Will need to check how much complexity this adds.

Overall even if this adds to much complexity we can still cherry-pick changes from here. Things like aria-disabled on non-html Button or a more robust test suite is probably a good thing.

@eps1lon
eps1lon force-pushed the feat/forwar-ref branch 5 times, most recently from e426edc to 786fcf5 Compare February 21, 2019 17:22
@mui-pr-bot

mui-pr-bot commented Feb 21, 2019

Copy link
Copy Markdown

@material-ui/core: parsed: +0.81% , gzip: +0.44%
@material-ui/lab: parsed: +0.70% , gzip: +0.71%

Details of bundle changes.

Comparing: 56fcebb...827b466

bundle parsed diff gzip diff prev parsed current parsed prev gzip current gzip
@material-ui/core +0.81% 🔺 +0.44% 🔺 369,244 372,227 91,572 91,973
@material-ui/core/Paper +0.37% 🔺 +0.40% 🔺 76,648 76,931 19,296 19,373
@material-ui/core/Paper.esm +0.01% 🔺 +0.03% 🔺 71,596 71,601 18,774 18,779
@material-ui/core/Popper 0.00% +0.02% 🔺 30,462 30,462 10,580 10,582
@material-ui/core/styles/createMuiTheme 0.00% 0.00% 17,286 17,286 5,717 5,717
@material-ui/core/useMediaQuery 0.00% +0.10% 🔺 2,486 2,486 1,050 1,051
@material-ui/lab +0.70% 🔺 +0.71% 🔺 183,226 184,506 50,374 50,730
@material-ui/styles +0.04% 🔺 +0.02% 🔺 57,708 57,733 16,229 16,233
@material-ui/system 0.00% 0.00% 17,062 17,062 4,485 4,485
Button +0.56% 🔺 +0.59% 🔺 99,076 99,633 26,477 26,634
Modal +0.01% 🔺 +0.03% 🔺 98,865 98,871 26,218 26,227
colorManipulator 0.00% -0.08% 3,232 3,232 1,297 1,296
docs.landing +0.07% 🔺 +0.17% 🔺 49,899 49,934 10,728 10,746
docs.main +0.19% 🔺 +0.17% 🔺 678,034 679,298 205,987 206,347
packages/material-ui/build/umd/material-ui.production.min.js +0.75% 🔺 +0.46% 🔺 320,385 322,787 84,772 85,165

Generated by 🚫 dangerJS against 827b466

@eps1lon
eps1lon force-pushed the feat/forwar-ref branch 3 times, most recently from 730364e to 6491c4c Compare February 24, 2019 19:31
@eps1lon
eps1lon marked this pull request as ready for review February 24, 2019 19:42

@oliviertassinari oliviertassinari left a comment

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.

Great work! I'm rebasing on next so we can merge :)

@oliviertassinari oliviertassinari mentioned this pull request Mar 1, 2019
1 task
This was referenced Mar 1, 2019
eps1lon added a commit that referenced this pull request Mar 4, 2019
Little bit different approach this time: Refactored tests first then implementation. Definitely still biased because I knew how the implementation would look like but still increased confidence in tests for me.

I need to investigate the shallow + disableLifeCycleIssue. This is causing enzyme to call componentDidMount if we wrap it in `forwardRef`.

Continues #14536
@eps1lon eps1lon mentioned this pull request Mar 4, 2019
@eps1lon
eps1lon deleted the feat/forwar-ref branch March 4, 2019 16:35
@eps1lon eps1lon mentioned this pull request Mar 4, 2019
eps1lon added a commit that referenced this pull request Mar 5, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking change Introduces changes that are not backward compatible.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants