The forwardRef function in React lets a parent component access a specific element or method inside a child component. This is helpful when the parent needs to do something like focus on an input field or start an animation in the child component directly.
Table of Content
What is forwardRef in React?
forwardRef is a higher-order function that allows you to pass a ref from a parent component to a child component, which then forwards that ref to a DOM node or another component. This enables parent components to access or manipulate a child component's DOM element.
React forwardRef allows to exposure of a DOM node to the Parent Component. It is a method that lets React forward the React refs to the child component. This technique is Forwarding Ref.
Syntax:
React.forwardRef((props, ref) => {})- Parameters: It takes a function with props and ref arguments.
- Return Value: This function returns a JSX Element.
How does forwardRef work in React?
In React, parent components typically use props to transfer data down to their children. Consider making a child component with a new set of props to change its behaviour. We need a way to change the behaviour of a child component without having to look for the state or re-rendering the component. We can do this by using refs. We can access a DOM node that is represented by an element using refs. As a result, we will make changes to it without affecting its state or having to re-render it.
When a child component needs to refer to its parent's current node, the parent component must have a way for the child to receive its ref. The technique is known as ref forwarding.
Example
Now write down the following code in the App.js file. Here, App is our default component where we have written our code.
//App.js
import React from 'react'
class App extends React.Component {
constructor(props) {
super(props)
this.aRef = React.createRef()
}
render() {
return (
<>
<Counter ref={this.aRef} />
<button onClick={() => { console.log(this.aRef) }}>
Ref
</button>
</>
)
}
}
const Counter = React.forwardRef((props, ref) => {
class Counter extends React.Component {
constructor(props) {
super(props)
this.state = {
count: 0
}
}
render() {
return (
<div>
Count: {this.state.count}
<button ref={ref} onClick={() => this.setState(
{ count: this.state.count + 1 })}>
Increment
</button>
</div>
)
}
}
return <Counter />
})
export default App
Output: Now open your browser and go to http://localhost:3000/, you will see the following output:
Explanation:
The explaination to above examples is as follows:
- The counter, residing within a function, provides closures to access props and ref parameters, ensuring effective encapsulation.
- The Counter component renders with a ref, establishing a connection to the button element using the ref attribute.
- The Counter component passes its ref to a child component, allowing seamless access to the button element.
- The App component renders the Counter component, initializing a ref and logging the HTMLButtonElement value on button click.
Exposing an Imperative Handle with useImperativeHandle
Instead of exposing a full DOM node, you can expose a custom imperative handle that only reveals specific methods or properties. This provides more control over what the parent component can do with the child component.
import { forwardRef, useImperativeHandle, useRef } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
const inputRef = useRef(null);
useImperativeHandle(ref, () => ({
focus() {
inputRef.current.focus();
},
scrollIntoView() {
inputRef.current.scrollIntoView();
},
}));
return <input ref={inputRef} {...props} />;
});
function Form() {
const inputRef = useRef(null);
function handleClick() {
inputRef.current.focus();
}
return (
<form>
<MyInput placeholder="Enter your name" ref={inputRef} />
<button type="button" onClick={handleClick}>Focus</button>
</form>
);
}
Common Pitfalls and Troubleshooting
- Ref is Null: If your ref is always null, ensure that the ref is passed down to a valid DOM node or a component that accepts refs. Also, make sure that the component is wrapped with forwardRef.
- Conditional Rendering: If a component conditionally renders a DOM node, the ref will only be available when that condition is true. Be cautious with conditional rendering inside components wrapped with forwardRef.
const MyInput = forwardRef(function MyInput({ showInput }, ref) {
return (
<div>
{showInput && <input ref={ref} />}
</div>
);
});
Best Practices
- Limit Ref Exposure: Only use forwardRef when necessary, such as when exposing low-level DOM elements like inputs or buttons. Avoid exposing DOM nodes in higher-level components.
- Keep Components Pure: In development mode, React’s Strict Mode will render your component twice to ensure that it is pure. If your component has side effects, make sure they don’t depend on being called once.
- Use useImperativeHandle for Controlled Exposure: If you only need to expose specific methods rather than the entire DOM node, use useImperativeHandle to limit what the parent component can access.