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
[WIP] Use forwardRef on most components. #1356
Conversation
Components that did not receive forwardRef: - Class-style components. - Functional components that use context (how to do?). - Deprecated components.
Which components don't make sense to have refs?Probably on the more complex components where users my want a ref to the component itself and not forwarded (or when the component's output is complex and there is no obvious child to forward the to). Maybe Should some of the class-style components be converted to functional components (such as Button)?Button, for example, has some functions which it binds to cache them. If it was converted to a function component the onClick would be create every render. I know Dan Abramov has some good reads on the topic and mostly comes down to perf testing, but since this is a library which can be used and many ways, I am not really sure how to provide the best perf to the end users. What branch should this go to?This seems like a feature that is wanted by many, go ahead and PR to master, I'll make another major release. |
Thanks for the quick response! I'll work through the TODOs and incorporate your suggestions, then put it up for proper review. |
These components only needed to be a class component because it had the getRef and focus/submit instance methods. getRef seems to be an undocumented vestige from an earlier implementation. It is also the only method by which this.ref could get set on the instance, which is what focus/submit would use. Removing getRef requires removing focus/submit, but this is okay because converting it to a functional component means that forwardRef will be able to supply the native DOM element that has the desired function.
This comes at the cost of losing this-binding, but it was already a stateless component so I figure the simplification of not having an innerRef is worth the negligible performance loss (if any).
Some of the tests required a workaround because Enzyme has incomplete support for forwardRef. These are the tests that actually use the ref attribute. Details on the workaround can be found at enzymejs/enzyme#1852
@seansfkelley I'm eagerly watching, keep up the good work! Let me know when it's ready. |
Thanks for the encouragement. :) There's been a lot of legwork to do to add it everywhere, though it's mostly working now. I'm struggling a bit with getting the tests to pass -- Enzyme will often inspect/return the |
Unfortunately I don't have the bandwidth to work on this for the foreseeable future, so I'm going to close it out to reduce noise. |
This PR is a work in progress and serves to demonstrate how this would be done.
Per React's official recommendation, this should be considered a breaking change and there is no recommended way to smooth the transition beyond that ("Conditionally applying
React.forwardRef
when it exists is also not recommended...")Changes in This PR
^16.3
.enzyme
andenzyme-adapter-react-16
to get support forforwardRef
.forwardRef
.innerRef
before can now be ref'd.innerRef
props.getRef
props.Open Questions
Which components don't make sense to have refs?Should some of the class-style components be converted to functional components (such asButton
)?What branch should this go to?TODO
innerRef
).Converted Components of Note
Most components that were converted were either already functional components.
Button
,Form
andInput
were class components, but I chose to convert them into functional components.Button
: The only reason this was a class component was to permit this-binding ofonClick
for performance reasons. I traded off simplicity and ergonomics of theref
prop for negligible performance cost.Form
/Input
: Both of these had what appeared to be cruftygetRef
props from an earlier implementation that did not appear to be used, along with wrappers around imperative methods (submit
andfocus
respectively). Assuming thatgetRef
is no longer desired, it is safe to convert to a forwarded ref component because the imperative methods will not be available directly on the ref'd DOM element.Unconverted Components
Some components were not converted, namely:
innerRef
or any other indication that they would benefit from forwarding. It would be easy to make this improvement in the future, but in the interest of keeping this PR focused on (1) functional components that would benefit the most from this change and (2) being mergeable in the near future, I opted to avoid them.The pattern for ref-forwarded components looks like this:
Components that have not been converted for this reason are:
Fixes #1082.