-
Notifications
You must be signed in to change notification settings - Fork 560
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 1 commit
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 |
---|---|---|
@@ -0,0 +1,104 @@ | ||
- Start Date: (fill me in with today's date, 2018-01-25) | ||
- RFC PR: (leave this empty) | ||
- React Issue: (leave this empty) | ||
|
||
# Summary | ||
|
||
Replace string refs with similar functionality to how they currently work but without many of the issues that string refs currently have (a list of them can be found at [#1373](/~https://github.com/facebook/react/issues/1373)). | ||
|
||
# Basic example | ||
|
||
The React.createRef() API will create an immutable object ref (where it's value is a mutable object referencing the actual ref). Accessing the ref value can be done via ref.value. An example of how this works is below: | ||
|
||
```js | ||
class MyComponent extends React.Component { | ||
divRef = React.createRef(); | ||
|
||
render() { | ||
return <div><input type="text" ref={this.divRef} /></div>; | ||
} | ||
|
||
componentDidMount() { | ||
this.divRef.value.focus(); | ||
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. How about function createRef() {
const ref = () => ref.value;
ref.value = null;
ref.__THIS_IS_A_REACT_REF_DO_NOT_TOUCH_THIS_OR_YOU_WILL_BE_FIRED__ = true;
return ref;
} 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. If object ref is also a function, there’s no easy way to distinguish which ref is which kind. Sure, React could use a field for this, but it is pretty confusing for third party libraries. 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. Also refs are an escape hatch. People shouldn’t be typing this so much. 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. 👍 for |
||
} | ||
} | ||
``` | ||
|
||
# Motivation | ||
|
||
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: | ||
|
||
```js | ||
class Foo { neverSet: boolean; } | ||
let foo = new Foo(); | ||
(foo.neverSet: boolean); // works | ||
``` | ||
|
||
# Detailed design | ||
|
||
Introduces `React.createRef()`: | ||
|
||
```js | ||
type RefObject<T> = { | ||
value: T | null | ||
}; | ||
|
||
interface React { | ||
createRef<T>(): RefObject<T>; | ||
} | ||
``` | ||
|
||
`createRef` requires a explicit type annotation to ensure type correctness. | ||
|
||
After `componentDidMount` lifecycle `divRef.value` will be filled with element/component reference: | ||
|
||
```js | ||
componentDidMount() { | ||
this.divRef.value instanceof HTMLDivElement === true | ||
} | ||
|
||
render() { | ||
return ( | ||
<div ref={this.divRef}> | ||
{this.props.children} | ||
</div> | ||
); | ||
} | ||
``` | ||
|
||
# Drawbacks | ||
|
||
Why should we *not* do this? Please consider: | ||
|
||
- implementation cost, both in term of code size and complexity | ||
- whether the proposed feature can be implemented in user space | ||
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. Here is a user space implementation const createRef = () => {
const ref = el => ref.value = el;
return ref;
};
React.createRef = createRef; 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. This is true. But in practice this pattern hasn’t caught on, and people keep using string refs in many places because they perceive functional refs as inconvenient. By adding this into the core we encourage people to adopt this pattern more strongly. 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. @gaearon Deprecation warning with link on documentation should have a similar effect. 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. If we deprecate string refs before adding this helper people will get very annoyed. Because function refs are more cumbersome to deal with. Yes, we can suggest this helper. But at that point we’re suggesting everybody to copy the same thing into their projects. What’s the point? It might as well be provided by React then. |
||
- 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?) | ||
|
||
There are tradeoffs to choosing any path. Attempt to identify them here. | ||
|
||
# Alternatives | ||
|
||
What other designs have been considered? What is the impact of not doing this? | ||
|
||
# Adoption strategy | ||
|
||
If we implement this proposal, how will existing React developers adopt it? Is | ||
this a breaking change? Can we write a codemod? Should we coordinate with | ||
other projects or libraries? | ||
|
||
# How we teach this | ||
|
||
Callback and object refs solve different problems and crossing over in what they offer too - much like how components in React do currently. | ||
|
||
Someone would likely choose an object ref when they only care about binding the object reference to the component instance/state they're in. Then the ref can be used for caching, or being passed to another component/handler in a lifecycle event. | ||
|
||
Callback refs are great when someone likes to have find grain control over the node at the point where it gets provides and removed (like a subscription) to react to it. | ||
|
||
# Unresolved questions | ||
|
||
Optional, but suggested for first drafts. What parts of the design are still | ||
TBD? |
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.
change
it's
toits