you can read my article on useRef hook
As a developer working with React, you’re always looking for ways to make your code more efficient and easier to maintain. That’s where forward refs come in.
With forward refs, you can easily access the DOM elements of your React components, allowing you to manipulate them in ways that weren’t previously possible.
This can be especially useful for controlling focus, text selection, or media playback. Plus, with forward refs, you can pass refs between components, making it easier to build complex and flexible UI systems.
In short, if you’re a React developer, you don’t want to miss out on the power of forward refs. So make sure to learn more about them today!
forward ref
forwardRef
lets your component expose a DOM node to the parent component with a ref.
Reference
forwardRef(render)
Call forwardRef()
to let your component receive a ref and forward it to a child component:
import { forwardRef } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
// ...
});
Parameters
render
: The render function for your component. React calls this function with the props andref
that your component received from its parent. The JSX you return will be the output of your component.
Returns
forwardRef
returns a React component that you can render in JSX. Unlike React components defined as plain functions, a component returned by forwardRef
is also able to receive a ref
prop.
let's take an example
Here we are increasing the font size, decreasing the font size, ad changing the color.
let’s deep dive into this
let’s start with the App.jsx
import Heading from "./components/Heading";
import { useRef } from "react";
import "./App.css";
function App() {
const headingRef = useRef(null);
const handleClick = () => {
headingRef.current.changeColor();
};
const increaseFontSize = () => {
headingRef.current.increaseFontSize();
};
const decreaseFontSize = () => {
headingRef.current.decreaseFontSize();
};
return (
<div className="App">
<Heading ref={headingRef} />
<button onClick={handleClick}>Click Me To Change The Text Color</button>
<button onClick={increaseFontSize}>Increase The Font Size</button>
<button onClick={decreaseFontSize}>Decrease The Font Size</button>
</div>
);
}
export default App;
Here we are just passing the headingRef
to the heading
component, and by using current
property we are manipulating the heading
component.
Now let's take a look at where we’re getting these changeColor() increaseFontSize() decreaseFontSize()
functions from.
now let’s look into Heading.jsx
for now, let’s make it short….
import React, { forwardRef, useImperativeHandle, useRef } from "react";
const Heading = forwardRef(function Heading(_props, ref) {
const newHeadingRef = useRef(null);
useImperativeHandle(
ref,
() => {
return {
increaseFontSize() {
newHeadingRef.current.style.fontSize = "100px";
}
};
},
[]
);
return (
<h1 style={{ color: "white" }} ref={newHeadingRef}>
Heading
</h1>
);
});
export default Heading;
Here we are using forward ref
so that we can get the ref from parent's component is App.jsx
we receive ref as a second parameter after props.
Now you’ll notice one thing we are not passing the ref to the tag h1
directly; instead, we are creating the new ref that is newHeadingRef
and we are passing it to the h1
tag.
And we are using a new hook that’s useImperativeHandle
you might be familiar with or not. so don’t worry; in the next step we are going to cover it why we are using it and what it.
useImperativeHandle
useImperativeHandle
is a React Hook lets you customize the handle exposed as a ref.
useImperativeHandle(ref, createHandle, dependencies?)
Reference
useImperativeHandle(ref, createHandle, dependencies?)
Call useImperativeHandle
at the top level of your component to customize the ref handle it exposes:
Parameters
ref
: Theref
you received as the second argument from theforwardRef
render function.createHandle
: A function that takes no arguments and returns the ref handle you want to expose. The ref handle you return have any type. Usually, you will return an object with the methods you want to expose.optional
dependencies
: The list of all reactive values referenced inside of thecreateHandle
code. Reactive values include props, state, and all the variables and functions declared directly inside your component body. If your linter is configured for React, it will verify that every reactive value is correctly specified as a dependency. The list of dependencies must have a constant number of items and be written inline like[dep1, dep2, dep3]
. React will compare each dependency with its previous value using theObject.is
comparison algorithm. If a re-render resulted in a change to some dependency, or if you did not specify the dependencies at all, yourcreateHandle
function will re-execute, and the newly created handle will be assigned to the ref.
const newHeadingRef = useRef(null);
useImperativeHandle(ref,() => {
return {
increaseFontSize() {
newHeadingRef.current.style.fontSize = "100px";
}
};
},[]);
Suppose you don’t want to expose the entire h1
DOM node, but you want to expose one of its methods: fontSize
To do this, keep the real browser DOM in a separate ref. Then use useImperativeHandle
to expose a handle with only the methods that you want the parent component to call:
Now, if the parent component gets a ref to h1
, it will be able to call the increaseFontSize
methods on it. However, it will not have full access to the underlying <h1>
DOM node.
so here is the full code for Heading.jsx
import React, { forwardRef, useImperativeHandle, useRef } from "react";
const Heading = forwardRef(function Heading(_props, ref) {
const newHeadingRef = useRef(null);
useImperativeHandle(
ref,
() => {
return {
changeColor() {
if (newHeadingRef.current.style.color === "white") {
newHeadingRef.current.style.color = "red";
return;
}
if (newHeadingRef.current.style.color === "red") {
newHeadingRef.current.style.color = "white";
return;
}
},
increaseFontSize() {
newHeadingRef.current.style.fontSize = "100px";
},
decreaseFontSize() {
newHeadingRef.current.style.fontSize = "50px";
},
};
},
[]
);
return (
<h1 style={{ color: "white" }} ref={newHeadingRef}>
Heading
</h1>
);
});
export default Heading;
Pitfall
Do not overuse refs. You should only use refs for imperative behaviors that you can’t express as props: for example, scrolling to a node, focusing a node, triggering an animation, selecting text, and so on.
If you can express something as a prop, you should not use a ref. For example, instead of exposing an imperative handle like { open, close }
from a Modal
component, it is better to take isOpen
as a prop like <Modal isOpen={isOpen} />
. Effects can help you expose imperative behaviors via props.
you can read my article on useRef hook
Hope you like it 🤗
Happy coding!
want to give suggestions:-