Hook Your Component: Unlocking the Power of UseImperativeHandle hook
Have you ever wanted complete control over your reusable component without relying solely on passing everything through props?
useImperativeHandle Hook: The useImperativeHandle hook allows a child component to expose multiple methods or properties to the parent via a ref object.
When building a reusable component, we often design it to support both controlled and uncontrolled behavior. For uncontrolled behavior, the state is maintained within the child component, while for controlled behavior, the state typically resides in the parent component. However, with the useImperativeHandle hook, managing state in the parent for a controlled component becomes unnecessary, as it allows us to directly access the child component's reference.
Let’s explore this with an example of a custom input component designed to support both controlled and uncontrolled behavior. The component accepts value (controlled), defaultValue (uncontrolled), onChange (callback method), and label as props.
In a traditional implementation for controlled behavior, the parent component manages state and updates it on every onChange event.
Here is the code reference of conventional implementation
Custom Input
Input Element
import React, { useState } from "react";
const Input = (props) => {
const { onChange, isControlled, defaultValue, label, value } = props;
const [internalValue, setInternalValue] = useState(defaultValue ?? "");
const handleChange = (e) => {
const newValue = e.target.value;
onChange?.(newValue);
if (!isControlled) {
setInternalValue(newValue);
}
};
const valueToDisplay = isControlled ? (value ?? "") : internalValue;
return (
<div className="input-container">
{label && <label>{label}</label>}
<input type="text" value={valueToDisplay} onChange={handleChange} />
</div>
);
};
export default Input;
Parent Element
const Parent = () => {
const [inputValue, setInputValue] = useState("Hello World!!!");
const onChange = (value) => {
setInputValue(value);
};
return (
<>
<Input
label={"Name"}
isControlled={true}
onChange={onChange}
value={inputValue}
/>
</>
);
}
With the power of the useImperativeHandle hook, maintaining extra state in the parent component becomes unnecessary. This hook allows you to customize the ref passed from the parent by adding specific properties, which can be either methods or values.
useImperativeHandle(ref, createHandle, dependencies)
领英推荐
Code reference using useImperativeHandle hook
Custom Input
import React, {
useState,
useImperativeHandle,
forwardRef,
} from "react";
const Input = (props, ref) => {
const { onChange, isControlled, defaultValue, label } = props;
const [value, setValue] = useState(defaultValue ?? "");
const handleChange = (e) => {
const newValue = e.target.value;
setValue(newValue);
onChange?.(newValue);
};
useImperativeHandle(
ref,
() => {
return {
updateInput(newValue: string) {
if (isControlled) setValue(newValue);
},
};
},
[isControlled]
);
return (
<div className="input-container">
{label && <label>{label}</label>}
<input type="text" value={value} onChange={handleChange} />
</div>
);
};
export default forwardRef(Input);
Parent Component
const Parent = () => {
const ref = useRef();
const resetInput = () => {
ref.current.updateInput("Hello World!!!");
};
return (
<>
<Input label={"Name"} isControlled={true} ref={ref} />
<button onClick={resetInput}>reset</button>
</>
);
};
In the above code, whenever the parent needs to update the input value, it simply calls the updateInput method exposed by the input component.
It has the following use cases:
You can find the code in my GitHub repository: AccessChildUseImperativeHandle.
Full Stack Expert in React, Java, and AWS | Architect of High-Performance Web Applications
4 个月Interesting