preface
This article is free translation, translation process mixed with my own understanding, if misleading, please give up reading.
Refs and the DOM
The body of the
Refs provides a way to access React Elements or native DOM nodes created in the Render method.
In a typical React data flow (top-down data flow), the props is the only way the parent can interact with its children. To interact with the child component, you need to pass a new props to the child component, prompting it to re-render. However, there are a number of scenarios that require us to dictate changes to child components outside of this props oriented data flow. The modified child component may be an instance of the React Component or a native DOM element. In both cases React provides a “safe hatch” to access them.
When do you use Refs?
The following business scenarios are appropriate:
- Manually manage focus, text selection or video and audio playback.
- Trigger an animation compulsively.
- Integration with third-party DOM class libraries.
If you can do it declaratively, don’t do it with Refs. For example, a popover can be opened and closed by passing an isOpenprop to the Dialog component, rather than by exposing the “open” and “close” methods.
Don’t abuse Refs
During actual development, if you run into implementation difficulties, your conventional wisdom might be to implement it with Refs first, whatever. In this case, you might as well calm down your mind and think more carefully. Can state be used to achieve this? If so, where in the component tree hierarchy should state be stored? Generally speaking, the appropriate level for storing state is the top-level component, which we used to call “Container Component.” Look at boosting your state to see how to do it.
Note that the following example has been updated. The updated example uses the react.createref () API. This API was introduced in React 16.3. If you’re still using the older React version, we recommend using callback Refs instead.
Object Refs
Create the Refs
Use react.createref () to create refs and attach to React element via the ref attribute. Normally, in component constructor, the refs created by react.createref () are assigned directly to an instance property of the component. This way, when the component is instantiated, you can use the reference elsewhere in the component. (That is, an attribute of a component instance refers to a Refs created via react.createref (), which is attached to the native DOM element or child component instance via the ref attribute. So we can access native DOM elements or child component instances through this instance attribute.)
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
return <div ref={this.myRef} />; }}Copy the code
Access to the Refs
This section describes how to access refs. As mentioned above, we hold references to refs through component instance properties. Refs is an object that has a current property. It is through this current attribute that we access the native DOM element or child component instance.
const node = this.myRef.current;
Copy the code
The value of the current attribute varies from case to case. This difference refers to the type of the React Component in which the REF attribute is located.
- DOM component. When the ref attribute is loaded in the DOM Component, the current value of the Refs object created with react.createref () will be a native DOM element.
- The custom component. Custom Components can be divided into class components and function components. Note that since function Component has no instance, it cannot use the ref attribute in this way (in this case, the method described in the section “Creating refs” above). The function Component can actually consume ref attributes, using the “ref forwarding” technique. So custom Component here means class Component. When the ref property is mounted to the Custom Component, the current property of the Refs object created by react.createref () will point to the component instance of the Custom Component.
The following example demonstrates the difference between the two.
1) Mount the REF attribute on the DOM Component
The following code uses the ref attribute to hold a reference to a DOM element:
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
// create a ref to store the textInput DOM element
this.textInput = React.createRef();
this.focusTextInput = this.focusTextInput.bind(this);
}
focusTextInput() {
// Explicitly focus the text input using the raw DOM API
// Note: we're accessing "current" to get the DOM node
this.textInput.current.focus();
}
render() {
// tell React that we want to associate the <input> ref
// with the `textInput` that we created in the constructor
return (
<div>
<input
type="text"
ref={this.textInput} />
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
Copy the code
When the component of the ref attribute is mounted to the page, the current attribute will be assigned a reference to the native DOM element. When the component is uninstalled, the current property is reset to NULL again. The update of the ref attribute value occurs before the component lifecycle functions componentDidMount and componentDidUpdate.
2) Mount the ref property on the class Component
If you want to wrap the above
in the parent component and want the mock component to automatically get focus when mounted. So, we can access the
instance through ref and manually focus the input box inside the corresponding component through the focusTextInput method of this instance.
class AutoFocusTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
componentDidMount() {
// in this case this.textinput. current points to
// An instance of the CustomTextInput component
this.textInput.current.focusTextInput();
}
render() {
return (
<CustomTextInput ref={this.textInput} />); }}Copy the code
Note that this is only useful if the
component is a class component.
class CustomTextInput extends React.Component {
// ...
}
Copy the code
3) Association between Refs and function Component
Note, third, that we no longer use the phrase “mount the REF attribute on the XXX component.” There is no use mounting the ref property directly to the Function Component. The use of the word “association” means that refs can be used in conjunction with function component. Again, function Component has no instance, so don’t mount the ref property directly on function Component:
function MyFunctionComponent() {
return<input />; } class Parent extends React.Component { constructor(props) { super(props); this.textInput = React.createRef(); } render() { // This will *not* work! return ( <MyFunctionComponent ref={this.textInput} /> ); }}Copy the code
If you want to mount the REF property directly on a component, you need to convert the component to a Class Component. This conversion is similar to how you would convert a component to a Class Component if you needed to use lifecycle functions or state.
However, as we mentioned above, the REF attribute can be used in conjunction with function Component. How to get married? That’s used in the implementation body of the Function Component. Note that ref genera are attached to DOM Component or class Component even when used in function Component:
function CustomTextInput(props) {
// textInput must be declared here so the ref can refer to it
let textInput = React.createRef();
function handleClick() {
textInput.current.focus();
}
return( <div> <input type="text" ref={textInput} /> <input type="button" value="Focus the text input" onClick={handleClick} /> </div> ); }Copy the code
Expose the DOM Refs to the parent component
In rare cases, you may want to access DOM elements in child components directly from the parent component. And in general, we don’t recommend it. Because doing so breaks the integrity of component encapsulation. But, you know, it works once in a while. For example, you might want to manually get focus in the input box or measure the position and size of DOM elements in a child component.
If you are using React 16.3 or above, we recommend that you use Ref Forwarding for your needs. Ref Forwarding lets components opt into exposing any child component’s Ref as Their own). You can refer to the Ref Forwarding document. This example will show you how to expose a REF reference to a DOM element in a child component to the parent component.
If you are using React 16.2 or below, or need a more flexible scheme than Ref Fowarding, you can use this scheme. The REF reference is explicitly passed through a prop name that is different from ref.
We recommend exposing DOM elements to the outside world as little as possible. However, it can be a useful “security hatch” to access the native DOM. Note that this approach to accessing the native DOM requires you to add some code to the child component. If you have no control over the implementation of the child component (i.e. inserting some code into it), then you are left with the last option – use findDOMNode(). In principle, findDOMNode() is already discouraged. This API is deprecated under StrictMode.
Callback Refs
React also supports other ways to set refs in addition to the above methods, one of which is called “callback Refs.” “Callback Refs” gives you more granular control when refs are assigned new values and reset.
Instead of using createRef () to create refs and pass it to the ref attribute, “callback refs” is passed to the ref attribute as a function, which is essentially a callback function. In the React Component callback function, you get a reference to the React Component instance or the native DOM element. As a general practice, use a component instance property to hold the reference for easy use everywhere:
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = null;
this.setTextInputRef = element= > {
this.textInput = element;
};
this.focusTextInput = (a)= > {
// Focus the text input using the raw DOM API
if (this.textInput) this.textInput.focus();
};
}
componentDidMount() {
// autofocus the input on mount
this.focusTextInput();
}
render() {
// Use the `ref` callback to store a reference to the text input DOM
// element in an instance field (for example, this.textInput).
return( <div> <input type="text" ref={this.setTextInputRef} /> <input type="button" value="Focus the text input" onClick={this.focusTextInput} /> </div> ); }}Copy the code
When the component is mounted to the page, React will pass a reference to the component instance or native DOM to the callback function. When the component is uninstalled, React passes null to the callback function again. React guarantees that refs updates will occur before componentDidMount and componentDidUpdate are called.
Just like the refs of the object type created by react.createref (), you can pass the refs of the function type along.
function CustomTextInput(props) {
return( <div> <input ref={props.inputRef} /> </div> ); } class Parent extends React.Component { render() { return ( <CustomTextInput inputRef={el => this.inputElement = el} /> ); }}Copy the code
In the example above, the Parent component
component as a prop called inputRef. The
component is then passed to the < Input> component with the real ref attribute. As a result, the instance attribute inputElement of
DOM element in the
component that we want to access.
Legacy API: String Refs
If you’ve used React before, you know that the value of the ref attribute can be a string, such as “textInput”. You can then access native DOM elements via this.refs.textinput. We strongly advise you not to use it again. Because it had some problems, and it had been abandoned. In a future release, it is likely that the implementation will be removed from the code.
Note that if you are using this.refs.textInput to access refs, we recommend using callback Pattern or the createRef API instead.
Notes for using callback Refs
If you assign an inline function directly to the ref attribute, the inline function will be called twice. The first call is null. The second time is called with a real DOM element. This is because each time the Render method is called, inline function creates a new function instance for the ref property. React needs to remove the old ref callback before setting the new one. To avoid this problem, you can bind the inline function to a class Component method, make it a reference, and then assign the reference to the ref property. Most of the time, though, inline function doesn’t cause much of a problem.