React Referencing Values with Refs
React works on the concept of breaking the code into smaller components, these small components help us to focus on specific areas. In React all the data flow happens through state and props. Whenever a state or prop changes the component is re-rendered. Sometimes there is a requirement to modify a component that is outside of the workflow, in such cases refs in react come to the rescue.
State and props are the default way to update a component in React. Whenever a state or prop is changed, the component is re-rendered.
React team has made refs in react which act as a bridge. This bridge allows a component to access or modify an element that the ref is attached to. Refs provides us with a way to bypass state updates and re-renders. This is very useful but we should not use refs as an alternative to state or props.
When we need to update a component without actually causing a re-render, refs in react come to the rescue. They allow us to directly modify the DOM element without using state. We know that react uses the state to trigger re-render but refs allow us to do modifications without triggering a re-render.
Both Functional and Class based components support refs. We will learn to create them in both types of components.
To create a ref in the class-based component we use the React.createRef() method. Refs are set to an instance property so that they can be used as a reference throughout the component. This all happens when the component is created.
Let's take an example to see how refs are created in React Class components
In functional components we utilize hooks, so we do not need to use the createRef() method, instead, we use the useRef() hook to create refs in functional components.
An example will help us to better understand this hook
A ref once created can be used throughout the lifecycle of the component.
useRef will return an object which has the property current. This current property holds the value that we pass to the ref. The thing about this object is that it is not tracked by React unlike the useState().
Let's make a simple counter using useRef,
What's special about ref is that the component does not re-render when the current changes. This is unlike what happens in useState() where the component re-renders when a state is modified.
Adding a Ref to a DOM Element
We don't need to manually update the DOM because react does it for us. However, sometimes there are cases where you want to update the DOM element for tasks like focusing a node, scrolling, etc.
Accessing a DOM node can be done using the useRef hook.
- Import the useRef hook
- Declare a ref inside the component
- Pass the ref to the DOM node as a ref attribute.
The useRef hook returns an object with a single property called current. Here, initially, the current property will be set to null. When a DOM node is created for this div, a reference to this node will be placed in myRef.current and this reference can be accessed throughout the component lifecycle.
Adding a Ref to a Class Component
In Class Components refs are created using the React.createRef() method. This method is used to create refs and then attach them to the component using the ref attribute.
In Class components, when the component is created, refs are assigned to an instance property.
Here is an example,
Refs and Function Components
In Functional components, we do not use a method instead we achieve similar tasks using hooks.
Here is an example,
How to Update a Refs Value?
The ref consists of an object which has a current property. The value we assign to a ref is stored inside this current property.
This property can be accessed using ref.current and we can simply assign the value to this property.
Let's take an example to see how values are assigned to a ref,
Exposing DOM Refs to Parent Components
A ref can be exposed to the parent component of the current component, i.e, the parent can have access to the ref of the child component. This is not a recommended method as it breaks the concept of encapsulation. This can be ideal for triggering focus or calculating the size or position of a child's DOM node.
However, adding a ref to a child component is not an ideal solution as by doing this we will only get a component instance rather than the DOM node. And this is not supported in Functional Components.
To get over these problems we use Ref Forwarding. Through this method, the component can expose any child component as its own.
Let's look at an example of ref forwarding,
React out of the box provides a forwardRef() method which allows us to define what element the ref will point at.
How Refs are Different from State?
|useState(initialValue) This returns the current value of a state variable and a setter function
|useRef(initialValue) This return an object with a current property
|Re-render is caused
|No Re-render is caused
|Can be modified using setState method
|current property need to be modified
|Can read state at any time
|Should not be read during rendering
When to Use Refs?
You may think that refs are very useful and we should use them at most places, but overusing refs or using them at inappropriate places can lead to bugs that can be tricky to solve.
There are certain scenarios where refs should be used,
- For manipulating DOM elements for tasks such as focus, scroll, etc.
- Triggering animations
- Integrations with third-party library
- Sorting timeout IDs and other objects that don't need to calculate JSX
A rule of thumb for using refs is if you need to store some value and that value doesn't impact the rerendering then we can choose refs.
Why You Shouldn’t Overuse Refs?
At first glance, you may want to use react refs for doing things in the React app. Before using the refs we should think about the position of the state, i.e position of the state in the component hierarchy.
Most of the time the most appropriate solution is to put the state inside a component at a higher level.
Callback refs give us more control over when refs are set and unset. In callback ref we pass a callback function to React.createRef() or useRef(). This callback function get's an element/node as an argument, which is then stored, manipulated, or used as per the requirement.
In this code, we have set the ref to null initially. The ref callback is executed when the component mounts the current node and is set as the value of the ref.
Caveats with Callback Refs
The ref callback will be invoked twice during updates if defined as an inline function. First, it is called with null and then again with the DOM element.
The reason for this is, with each render a new instance of the function is created, so react clears the old ref and creates a new one.
When you run this code you will observe that Rendering... is logged twice and when you click the button then Focus input is logged.
This article has detailed information about refs. In this article, we have learned
- refs in react are useful when we need to directly modify any DOM element.
- refs in react do not cause re-renders when they are modified.
- Refs can be used in both class components and functional components.
- Class Components - React.createRef() is used.
- Functional Components - useRef() hook is used.
- ref is an object that has a current property that stores the information we pass to a ref.
- Ref Forwarding is used to access a ref of the child component in the parent component.
- refs should be used in cases where changing the value of ref doesn't cause a re-render.
- A function can also be passed to a ref, this is called callback ref.