-
Notifications
You must be signed in to change notification settings - Fork 562
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
RFC: New createRef() API for ref objects #17
Changes from 2 commits
2647d5d
0b0d5d9
ea8070b
c709f3e
7d0d3c4
e249d45
9ae1b73
3bfb605
81a7d52
f9c52ed
a8ccc5a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,6 +26,25 @@ class MyComponent extends React.Component { | |
|
||
# Motivation | ||
|
||
Strings refs bind to the React's component's `currentOwner` rather than the parent. That's something that isn't statical analysable and leads to most of bugs. | ||
|
||
```js | ||
class ComponentA { | ||
render() { | ||
return ( | ||
// ref foo1 would bind to ComponentA | ||
<div ref="foo1"/> | ||
<ComponentB>{ | ||
// ref foo2 would bind to ComponentB | ||
// even though it's all in ComponentA's render | ||
() => <div ref="foo2" /> | ||
}</ComponentB> | ||
</div> | ||
); | ||
} | ||
} | ||
``` | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you mention other problems with string refs? For example lack of composability, or that they break “multiple Reacts” case. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you mean by "lack of composability" and how createRef fixes it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And what is the symptom of breaking with multiple reacts. Is there an error or just refs are not filled? |
||
This alternative API shouldn't provide any big real wins over callback refs - other than being a nice convenience feature. There might be some small wins in performance - as a common pattern is to assign a ref value in a closure created in the render phase of a component - this avoids that (even more so when a ref property is assigned to a non-existent component instance property). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would add here that the primary motivation is to encourage people to migrate off string refs. Callback refs meet some resistance because they are a bit harder to understand. We want to introduce this API primarily for people who love string refs today. |
||
|
||
One benefit would be more correct Flow types. People tend to type refs incorrectly because Flow doesn't enforce uninitialized class properties correctly: | ||
|
@@ -70,15 +89,61 @@ render() { | |
|
||
# Drawbacks | ||
|
||
Why should we *not* do this? Please consider: | ||
Callback refs are easier to use when child with ref and parent have different lifetime. With object ref it can be achieved with `componentDidUpdate` hook. As present in the example below callback refs are less verbose in this case. | ||
|
||
```js | ||
class ComponentA extends React.Component { | ||
setRef = (element) => { | ||
if (element) { | ||
// element is HTMLElement | ||
// ref is created | ||
} else { | ||
// element is null | ||
// ref is destroyed | ||
// cached reference is required to stop listening events | ||
} | ||
} | ||
|
||
divRef = React.createRef(); | ||
|
||
- implementation cost, both in term of code size and complexity | ||
- whether the proposed feature can be implemented in user space | ||
- the impact on teaching people React | ||
- integration of this feature with other existing and planned features | ||
- cost of migrating existing React applications (is it a breaking change?) | ||
componentDidMount() { | ||
if (this.divRef.value) { | ||
// ref is created | ||
} | ||
} | ||
|
||
componentDidUpdate() { | ||
if (this.divRef.value) { | ||
// ref is created | ||
} else { | ||
// ref is null | ||
// should be used cached reference to stop listening events | ||
} | ||
} | ||
|
||
componentWillUnmount() { | ||
if (this.divRef.value) { | ||
// ref is HTMLElement, not removed yet | ||
// cached reference is not required | ||
} | ||
} | ||
|
||
render() { | ||
if (this.props.enabled) { | ||
return ( | ||
<> | ||
<div ref={this.setRef} /> | ||
<div ref={this.divRef} /> | ||
</> | ||
); | ||
} else { | ||
return null; | ||
} | ||
} | ||
} | ||
``` | ||
|
||
There are tradeoffs to choosing any path. Attempt to identify them here. | ||
And still in most cases the same parent and ref lifetime makes code cleaner and simpler. | ||
|
||
# Alternatives | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most people don’t know what “current owner” is. Can you please explain the concept here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know how to describe it. Doesn't example below help?