React UseImperativeHandle

Table of contents

No heading

No headings in the article.

Hello Devs, today we'll be talking about one of the react's hook which is mis-understood or very difficult to understand by beginners and sometimes even by intermediate react developers, i.e useImperativeHandle (ta-daa). The word "Imperative" means "important"/ "pivotal"/ "urgent" thing, here the important thing for us is the Ref which this hook consumes

This hook works with refs, useImperativeHandle takes 3 arguments (ref, createHandle, [deps]), ref comes from parent component, createHandle is a function which returns us the methods which are defined, dependency array is array which contains any outside properties which are used inside of createhandle.

Lets take a look our first example for better understanding Link to First Example (Basic Usage of useImperativeHandle)

In the above example, we could see that we also have used forwardRef (a function which helps developers to pass a ref/forward to its children), talking about all parameters in detail, the first param ref is something which has a potential to change its-self over time, as mentioned earlier createHandle is a function which returns an object which contains properties that can be called from ref which is passed as an parameters

const Child=forwardRef((props, parentRef)=>{
useImperativeHandle(
parentRef, //ref coming from parent
()=>({
focused:false,
getValue:"",
getBlaBla:()=>{},
}),[] //empty dependency Array 
)
})

Talking about the methods present in the createHandle, can be called from parentRef, once useImperativeHandle is called, that is possible because what useImperativeHandle implicitly does is, it mutates the parentRef's current object with the object that createHandle returns.

const Child=forwardRef((props, parentRef)=>{
useImperativeHandle(
parentRef, //ref coming from parent
()=>({
focused:false,  // property-1
getValue:"",      // property-2
getBlaBla:()=>{}, // method-1
}),[] //empty dependency Array 
)
})

const Parent= ()=>{
const aRefFromParent=useRef();

const callRefMethods=()=>{
if(aRefFromParent.current){
aRefFromParent.current.getBlaBla();  
// we are calling this method in parent, this method was defined inside child component // using UseImperativeHandle.
}
}
return (
<div>this is parent component</div>
<Child ref={aRefFromParent}/>
)
}

Lastly we have a dependency array which contains elements which may change from time to time and this results to execution of createHandle function which updates the current Object of the parent again.

const Child=forwardRef((props, parentRef)=>{
useImperativeHandle(
parentRef, //ref coming from parent
()=>({
focused:props.property1,  // property-1
getValue:"",      // property-2
getBlaBla:()=>{}, // method-1
}),[props.property1] // dependency Array
)
})

// if the property1 changes, UseImperativeHandle will capture it and execute create handle again, which will be returning a new object will updated data

Usage of this would be rarely seen, as this hook, controls child element using a ref from parent component, there's also another way to control child element from parent in which we use vanilla javascript, which is a bad practice from react's POV.

NOTE: this hook can only be used with React.forwardRef, because useImperativeHandle requires a ref (rej Object) as its parameter

Below example shows us the error, if we try to use "useImperativeHandle" without forwardRef and pass a ref from parent as a prop

const Child = (props, ref) => {
  const myRef = useRef();

  // useImperativeHandleWillBeDefined here
  useImperativeHandle(
    ref,
    () => ({
      getValueIfPresent: () => {
        return myRef?.current.value;
      }
    }),
    []
  );

  return <input ref={myRef} />; // assigned to element
};

//////////////////////////////////

const Parent = () => {
  const myRef = useRef();
  const check = () => {
    if (myRef.current) {
      const val = myRef.current.getValueIfPresent(); // calling the method which is defined in Child Component
      console.log(val ? val : false);
    }
  };
  return (
    <>
      <button onClick={check}>check if it has value</button>
      <br />
      <Child ref={myRef} />
    </>
  );
};

// Output  ERROR :- "Cannot add property current, object is not extensible"

the reason it only works is forwardRef because, it passes a RefLikeObject to children

image (13).png