useRef and forwardRef in React
- In this article, let’s explore a pretty cool React hook, which is useRef. What do we use it for and some advice for you.
- The useRef hook is a function that returns an object with the current property initialized via the passed parameter. This returned object is mutable and will persist for the life of the component.
1 2 | const refContainer = useRef(initialValue); |
There are two main reasons we will use useRef: Access DOM nodes or React elements and store a mutable variable.
- But first to find out the useRef, we should find out what ref is.
What is ref?
- In React, ref is an attribute of a tag or an element that represents itself. ref gives us access to a mounted DOM node or React element. In vanila Javascript we work with DOM elements by calling document.getElementById (). With ref in React we don’t need to do that. The ref attribute will refer to the exact element needed.
1 2 | <input type="text" ref={textInput} /> |
ref accepts a variable or a function. If it is a function then this function will be run when the element is mounted.
1 2 | button ref={(element) => console.log(element)}>Send</button> |
Accessing DOM nodes or React elements
If you’ve been working with React for a while then you probably used ref for this. Below is an example of using ref in class component:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import React, { Component, createRef } from "react"; class CustomTextInput extends Component { textInput = createRef(); focusTextInput = () => this.textInput.current.focus(); render() { return ( <> <input type="text" ref={this.textInput} /> <button onClick={this.focusTextInput}>Focus the text input</button> </> ); } } |
And it’s the equivalent of a functional component
1 2 3 4 5 6 7 8 9 10 11 12 | import React, { useRef } from "react"; const CustomTextInput = () => { const textInput = useRef(); focusTextInput = () => textInput.current.focus(); return ( <> <input type="text" ref={textInput} /> <button onClick={focusTextInput}>Focus the text input</button> </> ); } |
Note that in a functional component we use useRef instead of createRef. If we create a ref using createRef in a functional component, React will create a new ref instance every re-render instead of keeping the instance intact throughout rendering.
Store a mutate variable
Both in class components and functional components that use hooks, we have two ways to keep data from being recreated between re-renders:
In class component
- In component state: Every time the state changes, the component will be re-rendered.
- In an instance variable: The variable will exist for the life of the component. Changing the instance of the variable will not cause a re-render.
In a functional component
- In the state of the component: useState or useReducer. Updating the state variable will cause a re-render component.
- In a ref: Equivalent to instance variables in class component. Mutating the .current property will not cause a re-render.
It is important to note that in a React application, it is not necessary to re-render the component when we update a value. See the example below
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import React, {useRef, useState} from 'react' const MessageInputComponent = () => { const [message, setMessage] = useState("") const sentMessage = useRef(0); const sendMessage = () => { if(sentMessage.current === 3){ return alert("Message Limit Reached") } sentMessage.current += 1 //code to handle sending message } return( <div> <input onChange = {(e) => setMessage(e.target.value)} value={message}/> <button onClick={sendMessage}>Send</button> </div> ) } export default MessageInputComponent |
Here, we use both useState and useRef. The message state represents the value of the input, when the input changes, the components will re-render and update the value for the input. sentMessage ref counts the number of times the message is sent, and it does not require the component to re-render when the sentMessage changes.
Notice how the useRef value changes. It doesn’t need to use the set method like the useState side. To change the value, we simply need to directly change the value stored in the useRef’s current property.
What is forwardReef
As we have learned above, the ref helps us to access an element, so can it access a React component or not? Try it out
1 2 3 4 | import React from "react"; const Input = () => <input type="text" style={style} />; export default Input; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import React, { useRef, useEffect } from "react"; import Input from "./Input"; function App() { const inputRef = useRef(null); useEffect(() => { if (inputRef.current) { inputRef.current.focus(); } console.log({ inputRef }); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ( <div> <Input ref={inputRef} /> </div> ); } export default App; |
We will receive a message that references the Input Component to null
To fix this problem we use forwardRef as a HOC for the Input Component
1 2 3 4 | import React, { forwardRef } from "react"; const Input = (props, ref) => <input ref={ref} type="text" style={style} />; export default forwardRef(Input); |
Summary
- A ref is created when the component is mounted. ref is assigned to an element, if you want to pass ref through the component, you use forwardRef.
- Ref can be used to access the DOM node or React element. Also used to store mutable variables without re-rendering the component.
Hope the article helps people understand ref, useRef and forwardRef and how to use them. See you in other posts