Refs make it possible to access DOM nodes directly within React. This comes in handy in situations where, just as one example, you want to change the child of a component. Let’s say you want to change the value of an <input>
element, but without using props or re-rendering the whole component.
That’s the sort of thing refs are good for and what we’ll be digging into in this post.
How to create a ref
createRef()
is a new API that shipped with React 16.3. You can create a ref by calling React.createRef()
and attaching a React element to it using the ref
attribute on the element.
class Example extends React.Component { constructor(props) { super(props) // Create the ref this.exampleRef = React.createRef() } render() { return ( // Call the ref with the `ref` attribute ) }
}
We can “refer” to the node of the ref created in the render method with access to the current attribute of the ref. From the example above, that would be this.exampleRef.current
.
Here’s an example:
See the Pen React Ref – createRef by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.
class App extends React.Component { constructor(props) { super(props) // Create the ref this.textInput = React.createRef(); this.state = { value: '' } } // Set the state for the ref handleSubmit = e => { e.preventDefault(); this.setState({ value: this.textInput.current.value}) }; render() { return ( React Ref - createRef
// This is what will update Value: {this.state.value}
); }
}
This is a component that renders some text, an input field and a button. The ref is created in the constructor and then attached to the input element when it renders. When the button is clicked, the value submitted from the input element (which has the ref attached) is used to update the state of the text (contained in an H3 tag). We make use of this.textInput.current.value
to access the value and the new state is then rendered to the screen.
Passing a callback function to ref
React allows you to create a ref by passing a callback function to the ref
attribute of a component. Here is how it looks:
<input type="text" ref={element => this.textInput = element} />
The callback is used to store a reference to the DOM node in an instance property. When we want to make use of this reference, we access it using:
this.textInput.value
Let’s see how that looks in the same example we used before.
See the Pen React Ref – Callback Ref by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.
class App extends React.Component { state = { value: '' } handleSubmit = e => { e.preventDefault(); this.setState({ value: this.textInput.value}) }; render() { return ( React Ref - Callback Ref
Value: {this.state.value}
); }
}
When you make use of callback like we did above, React will call the ref callback with the DOM node when the component mounts, when the component un-mounts, it will call it with null.
It is also possible to pass ref from a parent component to a child component using callbacks.
See the Pen React Ref – Callback Ref 2 by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.
Let’s create our “dumb” component that will render a simple input:
const Input = props => { return ( );
};
This component is expecting inputRef
props from its parent component which is then used to create a ref to the DOM node.
Here’s the parent component:
class App extends React.Component { state = { value: '' }; handleSubmit = event => { this.setState({ value: this.inputElement.value }); }; render() { return ( React Ref - Callback Ref
Value: {this.state.value}
(this.inputElement = el)} /> ); }
}
In the App component, we want to obtain the text that is entered in the input field (which is in the child component) so we can render it. The ref is created using a callback like we did in the first example of this section. The key lies in how we access the DOM of the input element in the Input component from the App component. If you look closely, we access it using this.inputElement
. So, when updating the state of value in the App component, we get the text that was entered in the input field using this.inputElement.value
.
The ref attribute as a string
This is the old way of creating a ref and it will likely be removed in a future release because of some issues associated with it. The React team advises against using it, going so far as to label it as “legacy” in the documentation. We’re including it here anyway because there’s a chance you could come across it in a codebase.
See the Pen React Ref – String Ref by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.
Going back to our example of an input whose value is used to update text value on submit:
class App extends React.Component { state = { value: '' } handleSubmit = e => { e.preventDefault(); this.setState({ value: this.refs.textInput.value}) }; render() { return ( React Ref - String Ref
Value: {this.state.value}
); }
}
The component is initialized and we start with a default state value set to an empty string (value='’
). The component renders the text and form, as usual and, like before, the H3 text updates its state when the form is submitted with the contents entered in the input field.
We created a ref by setting the ref
prop of the input field to textInput
. That gives us access to the value of the input in the handleSubmit()
method using this.refs.textInput.value
.
Forwarding a ref from one component to another
**Ref forwarding is the technique of passing a ref from a component to a child component by making use of the React.forwardRef()
method.
See the Pen React Ref – forward Ref by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.
Back to our running example of a input field that updates the value of text when submitted:
class App extends React.Component { constructor(props) { super(props) this.inputRef = React.createRef(); this.state = { value: '' } } handleSubmit = e => { e.preventDefault(); this.setState({ value: this.inputRef.current.value}) }; render() { return ( React Ref - createRef
Value: {this.state.value}
); }
}
We’ve created the ref in this example with inputRef
, which we want to pass to the child component as a ref attribute that we can use to update the state of our text.
const Input = React.forwardRef((props, ref) => ( <input type="text" ref={ref} />
));
Here is an alternative way to do it by defining the ref outside of the App component:
const Input = React.forwardRef((props, ref) => ( <input type="text" ref={ref} />
));
const inputRef = React.createRef();
class App extends React.Component { constructor(props) { super(props) this.state = { value: '' } } handleSubmit = e => { e.preventDefault(); this.setState({ value: inputRef.current.value}) }; render() { return ( React Ref - createRef
Value: {this.state.value}
); }
}
Using ref for form validation
We all know that form validation is super difficult but something React is well-suited for. You know, things like making sure a form cannot be submitted with an empty input value. Or requiring a password with at least six characters. Refs can come in handy for these types of situations.
See the Pen React ref Pen – validation by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.
class App extends React.Component { constructor(props) { super(props); this.username = React.createRef(); this.password = React.createRef(); this.state = { errors: [] }; } handleSubmit = (event) => { event.preventDefault(); const username = this.username.current.value; const password = this.password.current.value; const errors = this.handleValidation(username, password); if (errors.length > 0) { this.setState({ errors }); return; } // Submit data }; handleValidation = (username, password) => { const errors = []; // Require username to have a value on submit if (username.length === 0) { errors.push("Username cannot be empty"); } // Require at least six characters for the password if (password.length < 6) { errors.push("Password should be at least 6 characters long"); } // If those conditions are met, then return error messaging return errors; }; render() { const { errors } = this.state; return ( React Ref Example