36. useRef Hook in React
useRef is a hook that is specially designed to work with DOM elements. It allows you to create a reference to a DOM element and access it directly in your components so you can modify it or read its properties as needed.
Another important distinction is that useRef holds the same reference across re-renders of the component Similarly, useState also retains its value between renders. However, updating a state value with useState will always trigger a re-render, whereas updating the value of a useRef does not. Its explained in the below example in ## 2. Advanvced Usage
1. Basic Usage
1. Import useRef
import { useRef } from 'react';
2. Create a Reference
const myRef = useRef();
3. Attach to DOM Elements
function MyComponent() {
const myRef = useRef();
return (
<div ref={myRef}>
<h1>useRef Example</h1>
</div>
);
}
4. Accessing the Reference
// Access the DOM element using .current
const element = myRef.current;
use
myRef.current.value
for input elements. usemyRef.current.checked
for checkbox elements. usemyRef.current.focus()
to focus on an element. usemyRef.current.style
to access the style object of an element.
Note: While you can access DOM elements directly with useRef, it’s recommended to avoid manipulating them directly unless necessary.
Practical Example: Form Input
Here’s a complete example showing how to use useRef with form inputs:
import { useRef, useState } from 'react';
function InputExample() {
const inputRef = useRef();
const [inputValue, setInputValue] = useState('');
const handleClick = () => {
setInputValue(inputRef.current.value);
inputRef.current.value = ''; // Clear input after reading
};
return (
<div>
<input type="text" ref={inputRef} placeholder="Type something..." />
<button onClick={handleClick}>Save Input</button>
<p>Saved value: {inputValue}</p>
</div>
);
}
Explanation:
- We create a reference to the input element using useRef.
- When the button is clicked, we read the input value using inputRef.current.value and update the state.
- We clear the input field by setting inputRef.current.value to an empty string.
2. Advanced Usage of useRef
and useState
useRef
anduseState
are both hooks used in React.- Both hooks help us store values, but they behave differently:
useState
: The value is persistent across re-renders, but updating it triggers a re-render.useRef
: The value is also persistent across re-renders, but updating it does not trigger a re-render.
2.1. Managing Previous Values in a Stopwatch
Here’s an example of how to use useRef
and useState
to build a simple stopwatch in React:
import { useRef, useState } from 'react';
function Stopwatch() {
const timerRef = useRef(0); // To store the timer ID
const [time, setTime] = useState(0); // To store the current time
const [isRunning, setIsRunning] = useState(false); // To track if the stopwatch is running
// Start the timer when the Start button is clicked
const startTimer = () => {
if (!isRunning) {
timerRef.current = setInterval(() => {
setTime((time) => time + 1); // Increment the time every second
}, 1000);
setIsRunning(true); // Set the timer as running
}
};
// Stop the timer when the Stop button is clicked
const stopTimer = () => {
clearInterval(timerRef.current); // Clear the interval to stop the timer
setIsRunning(false); // Set the timer as not running
};
// Reset the timer when the Reset button is clicked
const resetTimer = () => {
clearInterval(timerRef.current); // Clear the interval to stop the timer
setIsRunning(false); // Set the timer as not running
setTime(0); // Reset the time to 0
};
return (
<div>
<h1>Stopwatch</h1>
<p>Time: {time} seconds</p>
<button onClick={startTimer}>Start</button>
<button onClick={stopTimer}>Stop</button>
<button onClick={resetTimer}>Reset</button>
</div>
);
}
Explanation
Creating
useRef
anduseState
variables:timerRef
: This holds the timer ID (returned bysetInterval
). We useuseRef
here because we don’t need the component to re-render when the timer ID changes.time
: This stores the current time value of the stopwatch, and we useuseState
for this because we want the component to re-render whenever the time updates.isRunning
: This tracks whether the stopwatch is running or not.
Starting the timer (Start Button):
- When the “Start” button is clicked, we check if the timer is already running. If it isn’t, we start it by calling
setInterval
, which increments the time value every second. setInterval
returns an interval ID that we store intimerRef.current
. This ID will be used to stop the timer later.
- When the “Start” button is clicked, we check if the timer is already running. If it isn’t, we start it by calling
Stopping the timer (Stop Button):
- When the “Stop” button is clicked, we clear the interval using
clearInterval(timerRef.current)
to stop the timer. - We also set
isRunning
tofalse
to indicate that the timer is no longer running.
- When the “Stop” button is clicked, we clear the interval using
Resetting the timer (Reset Button):
- When the “Reset” button is clicked, we clear the interval to stop the timer, set
isRunning
tofalse
, and reset thetime
back to 0.
- When the “Reset” button is clicked, we clear the interval to stop the timer, set
Why useRef
?
Why not just use a regular variable?
- A regular variable inside the component would reset every time the component re-renders. Since the timer is being updated every second, this would cause issues because the value would be reset each time, and the timer wouldn’t function correctly.
Why
useRef
overuseState
for the timer ID?- We use
useRef
to store the timer ID because updating it withuseState
would trigger a re-render every time the timer ID changes. Since the timer is running independently, we don’t need a re-render, souseRef
is more efficient here.
- We use
Key Takeaways
useRef
is useful for storing values that don’t require the component to re-render when they change.useState
is used for values that should trigger a re-render when updated, like the time value in our stopwatch.useRef
does not cause a re-render when its value changes, making it ideal for holding values like the interval ID that don’t need to affect the UI.
Common Use Cases
- Storing previous values
- Managing focus, text selection, or media playback
- Integrating with third-party DOM libraries
- Accessing underlying DOM elements
Isolation in Components: Any variable that is declared inside a component is isolated to that component. It is not shared with other components unless explicitly passed as props. This isolation helps maintain the encapsulation and modularity of components in React.
37.Forwarding Refs in React
Forwarding refs is a technique in React that allows you to pass a ref from a parent component to a child component. This is useful when you want to access the DOM element of a child component from a parent component.
1. Basic Usage
1.Import useRef
import { useRef } from 'react';
2. Create a Ref in the Parent Component
const myRef= useRef();
3. Pass the Ref to the Child Component
<MyChildComponent ref={myRef} />
4.Import forward ref in child component
import React, { forwardRef } from 'react';
5. Using forwardRef to Forward the Ref
const MyChildComponent = forwardRef((props, ref) => {
return <input ref={ref} />;
});
6. Accessing the Ref in the Parent Component
const element = myRef.current.value;
Practical Example: Input Focus
to take input from user and focus on the input field of the child component from the parent component.
import React, { useRef } from 'react';
const InputComponent = forwardRef(({title}, ref) => {
return<> <h1>{title}</h1>
<input ref={ref}/>
</>
});
function ParentComponent() {
const inputRef = useRef();
const handleClick = () => {
inputRef.current.focus();
console.log(inputRef.current.value);
};
return (
<div>
<InputComponent ref={inputRef} title="ENter the Input"/>
<button onClick={handleClick}>Focus Input</button>
</div>
);
}
38.useImperativeHandle in React
In large-scale applications, we generally avoid using refs directly to access child components. Instead, we use the useImperativeHandle hook to expose specific methods from the child component to the parent component. This approach allows the developer working on the parent component to interact with the child component’s methods without needing to understand its internal implementation. Additionally, this separation enables the developer of the child component to modify or update the child component independently, without affecting the parent component. Since only the exposed methods are used by the parent, changes to the internal structure of the child component do not disrupt the parent-child interaction.This is specially used in forwardRef to expose methods of child component to parent component.
Basic Usage
1.Pass a Ref to the Child Component
import {useRef} from 'react';
function App() {
const childRef = useRef();
return <ChildComponent ref={childRef} />;
}
=> Here, we create a ref using useRef and pass it to the ChildComponent using the ref prop.
2. Use useImperativeHandle in the Child Component with forwardRef to expose methods
import { useImperativeHandle, forwardRef } from 'react';
const ChildComponent = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
focus: () => {
// Focus on the input element
},
reset: () => {
// Reset the input element
},
inputValue:'vaule'
}),[dependencies]);
return <input />;
});
In the ChildComponent, we use the useImperativeHandle
hook to expose the focus and reset methods to the parent component. The ref
object is passed as the first argument, and the second argument is a function
that returns an object containing the methods to be exposed.
dependencies
is an optional array of values that, when changed, will trigger the re-evaluation of the function that returns the methods. If the dependencies array is not provided, the function will be called on every render and empty array will call only once in entire lifecycle.
3. Access the Exposed Methods in the Parent Component
function App() {
const childRef = useRef();
const handleClick = () => {
childRef.current.focus();
};
const resetClick = () => {
childRef.current.reset();
};
return (
<>
<ChildComponent ref={childRef} />
<button onClick={handleClick}>Focus Input</button>
<button onClick={resetClick}>Reset Input</button>
</>
);
}
In the parent component, we can access the exposed methods using the current
property of the ref object. In this example, we call the focus
method when the “Focus Input” button is clicked.
Practical Example: Use forwardRef and useImperativeHandle to Expose Methods for reset the input field and get the value of the input field.
import {useRef} from 'react';
function App(){
const childRef = useRef();
const handleClick = () => {
childRef.current.focus();
};
const resetClick = () => {
childRef.current.reset();
};
const getValue = () => {
console.log(childRef.current.value);
};
return (
<>
<ChildComponent ref={childRef} />
<button onClick={handleClick}>Focus Input</button>
<button onClick={resetClick}>Reset Input</button>
<button onClick={getValue}>Get Value</button>
</>
);
}
export default App;
import { useImperativeHandle, forwardRef,useRef } from 'react';
const ChildComponent = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
reset: () => {
inputRef.current.value = '';
},
value: inputRef.current.value
}),[]);
return <input ref={inputRef} />;
});
export default ChildComponent;
=> By using useImperativeHandle
, we can expose specific methods from the child component to the parent component, allowing for more controlled interactions between the components such as focusing on the input field, resetting the input field, and getting the value of the input field in this example.
=> By using this approach, we can maintain a clear separation of concerns between the parent and child components, making it easier to manage and update the components independently.
37. Portals in React
Portals in React provide a way to render children components outside the DOM hierarchy of the parent component. This allows you to render a child component at a different location in the DOM, such as at the root level or inside a specific container, without affecting the parent’s layout or styles.
Importing portal from react-dom
import { createPortal } from 'react-dom';
using createPortal
const MyPortal = ({title}) => {
return createPortal(
<>
<h1>{title}</h1>
<p>This is a portal</p>
<>,
document.getElementById('portal-root'));
};
=>When the MyPortal component is rendered, the content will be rendered inside the element with the id ‘portal-root’, which can be located anywhere in the DOM.