Hook Your Component: Unlocking the Power of UseImperativeHandle hook

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)        

  • ref: The reference object passed from the parent.
  • createHandle: A function that returns the properties (methods or values) you want to expose to the parent.
  • dependencies: An optional array of dependencies; when these change, the createHandle function re-runs to update the exposed properties.


Accessing child properties using useImperativeHandle

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:

  1. Updating the child's state from the parent without passing props.
  2. Accessing the child's state at any moment without requiring a callback from the child.
  3. Triggering an event in the child from the parent.


You can find the code in my GitHub repository: AccessChildUseImperativeHandle.


Annabathula V.

Full Stack Expert in React, Java, and AWS | Architect of High-Performance Web Applications

4 个月

Interesting

回复

要查看或添加评论,请登录

Ram Girish Reddy Karri的更多文章

社区洞察

其他会员也浏览了