Forwarding Refs - anastasiamexa/react-complete-guide-course-resources GitHub Wiki
In React, forwarding a ref refers to a technique where a child component allows its parent or ancestor component to access and interact with the child's DOM element or React component. This is often useful in cases where you have a composite component (composed of multiple sub-components) and the parent component needs to manage or interact with the child's internal details.
Here's a step-by-step explanation of how forwarding a ref works:
1. Create a Ref in the Parent Component:
In the parent component, you create a ref using the useRef
hook or React.createRef()
.
import React, { useRef } from 'react';
function ParentComponent() {
const childRef = useRef();
// ...
}
2. Pass the Ref to the Child Component:
You pass the ref to the child component as a prop.
import React, { useRef } from 'react';
import ChildComponent from './ChildComponent';
function ParentComponent() {
const childRef = useRef();
return <ChildComponent forwardedRef={childRef} />;
}
3. Access the Ref in the Child Component:
In the child component, you use the forwardedRef
prop and the React.forwardRef
function to forward the ref to a specific DOM element or another child component.
import React, { forwardRef } from 'react';
const ChildComponent = forwardRef((props, ref) => {
// Use the forwarded ref to attach it to a DOM element or another component
return <div ref={ref}>Child Component</div>;
});
export default ChildComponent;
The forwardRef
function takes a render function as its argument, and this function receives the ref as its second argument. You use this ref to attach it to the desired element or component.
4. Access the Ref in the Parent Component:
Now, in the parent component, you can access and interact with the child's DOM element or component using the ref.
import React, { useRef, useEffect } from 'react';
import ChildComponent from './ChildComponent';
function ParentComponent() {
const childRef = useRef();
useEffect(() => {
// Access and interact with the child's DOM element or component
console.log(childRef.current);
}, [childRef]);
return <ChildComponent forwardedRef={childRef} />;
}
By forwarding the ref from the parent to the child and then from the child to the specific DOM element or component, you allow the parent component to manage or interact with the internals of the child component.
This pattern is especially useful when creating reusable components or when you need to coordinate actions between parent and child components.
// Input.js
import { forwardRef } from 'react';
const Input = forwardRef(function Input({ type, label }, ref) {
return (
<p className="control">
<label>{label}</label>
<input type={type} ref={ref} />
</p>
);
});
export default Input;
Explanation:
- The
Input
component usesforwardRef
to forward the ref to the input element inside it. - The ref is passed as a second parameter to the functional component.
- The input element uses this forwarded ref so that the parent component can access the underlying DOM element.
// App.js
import { useRef } from 'react';
import Input from './Input';
export const userData = {
name: '',
email: '',
};
export function App() {
const nameInput = useRef();
const emailInput = useRef();
function handleSaveData() {
// Access the values of the input elements using the refs
userData.name = nameInput.current.value;
userData.email = emailInput.current.value;
console.log(userData);
}
return (
<div id="app">
{/* Forwarding the refs to the Input components */}
<Input ref={nameInput} type="text" label="Your Name" />
<Input ref={emailInput} type="email" label="Your E-Mail" />
<p id="actions">
<button onClick={handleSaveData}>Save Data</button>
</p>
</div>
);
}
Explanation:
- In the
App
component, twouseRef
hooks (nameInput
andemailInput
) are created. - These refs are then passed to the respective
Input
components as the ref prop. - When the "Save Data" button is clicked, the
handleSaveData
function is called. - Inside this function, the values of the input elements are accessed using the refs (
nameInput.current.value
andemailInput.current.value
). - The values are then stored in the
userData
object and logged to the console.
This example illustrates how forwarding refs allows the parent component (App
) to interact with the internal elements of the child components (Input
) without exposing the details of the child components' implementation.
The useImperativeHandle
is a React Hook that allows you to customize the instance value that is exposed when using React.forwardRef
. It is often used in conjunction with forwardRef
to provide a more controlled and specific API to parent components when interacting with child components.
The primary use case for useImperativeHandle
is to expose specific methods or values from a child component's instance to its parent, allowing the parent to interact with the child in a controlled manner.
import { forwardRef, useImperativeHandle, useRef } from 'react';
const Form = forwardRef(function Form({}, ref) {
const formRef = useRef();
useImperativeHandle(ref, () => {
return {
clear() {
formRef.current.reset();
},
};
});
return (
<form ref={formRef}>
{/* ... input fields and other form elements */}
</form>
);
});
export default Form;
- The
Form
component usesforwardRef
to allow the parent component to access its internalformRef
. - Inside
useImperativeHandle
, it exposes aclear
method through the forwarded ref. This method callsreset
on the form element, effectively clearing the form inputs.
import { useRef } from 'react';
import Form from './Form';
export function App() {
const form = useRef();
function handleRestart() {
form.current.clear();
}
return (
<div id="app">
<button onClick={handleRestart}>Restart</button>
<Form ref={form} />
</div>
);
}
- The
App
component creates a ref (form
) usinguseRef
. - When the "Restart" button is clicked, the
handleRestart
function is called, which then invokes theclear
method exposed by theForm
component through the forwarded ref.
This pattern allows the parent component to have controlled access to specific functionality within the child component. In this case, the parent can trigger a form reset without having to directly manipulate the DOM or manage the internal state of the Form
component. The use of forwardRef
and useImperativeHandle
provides a clean and explicit way to expose functionality from child to parent while maintaining encapsulation.