ReactJs: Expose the functions/variables on children to parent component with useImperativeHandle
Hi everyone, I think it’s been a while ya when the last time I have posted the tutorials. Now, I’m a bit missed to share my journey or story on the tech stuff.
So, to save our time, I’m going to create tutorial about reactjs on particulary is feature of useImperativeHandle. But, before we dive into the tutorial, have you ever heard about this thing?
or have you ever faced some causes on your project that you want to accessing function/variable on the children from the parent component.
Let’s take a look on this image, how did you do to resolve this problem?
Without useImperativeHandle
I think, most of us are familiar with the approach to drill down props and state from parent to child or otherwise from child to parent with the same way. Basically, I usually do this one before I know about useImperativeHandle.
import { forwardRef, useImperativeHandle, useRef, useState } from 'react';
const Parent = () => {
const [stateParent, setStateParent] = useState('State Parent');
const [isClicked, setIsClicked] = useState(false);
const [stateFromChildren, setStateFromChildren] = useState('');
const getValueFromChild = (data: any) => {
console.log(data, 'data');
// will be received value of stateA
setStateFromChildren(data);
};
const checkValue = () => {
setIsClicked((prev) => !prev);
};
return (
<div>
<button
type="button"
onClick={checkValue}
style={{ border: '1px solid black' }}
>
Check Data From Parent
</button>
{isClicked && <p>This is value from children: {stateFromChildren}</p>}
<Children stateParent={stateParent} drillUpShowA={getValueFromChild} />
</div>
);
};
const Children = (props: any) => {
const [stateA, setStateA] = useState('A');
const sendValueShowA = () => {
props?.drillUpShowA && props?.drillUpShowA(stateA);
};
return (
<div>
<br />
<button
type="button"
onClick={sendValueShowA}
style={{ border: '1px solid black' }}
>
Send Value Show A
</button>
<p>This is state on children: {stateA}</p>
<p>This is state parent on children: {props?.stateParent} </p>
</div>
);
};
export default Parent;
Are you familiar with that approach? I guess yes, you’re familiar with that.
Here the completed project of the above codes.
Demo — not used useImperativeHandle (forked) — StackBlitz
With useImperativeHandle
Allright guys, I think we can use another approach to make our code getting better and better. This’s a feature from ReactJs itself, yap we can use useImperativeHandle hooks.
Actually, we are not only using useImperativeHandle hooks but also useRef hooks. So, let’s refactor that code to be a better way.
import { forwardRef, useImperativeHandle, useRef, useState } from 'react';
const Parent = () => {
const [stateParent, setStateParent] = useState('State Parent');
const [stateFromChildren, setStateFromChildren] = useState('');
const [isClicked, setIsClicked] = useState(false);
const ref = useRef<any>(null);
const getValueFromChild = () => {
if (ref.current) {
const valueStateA = ref.current.getValueStateA();
console.log(valueStateA);
// will be received value of stateA from ref
setStateFromChildren(valueStateA);
setIsClicked((prev) => !prev);
}
};
const ChildrenProps = {
stateParent,
};
return (
<div>
<button
style={{ border: '1px solid black' }}
type="button"
onClick={getValueFromChild}
>
Check Data
</button>
{isClicked && (
<p>
This is value from children and render on parent component:
{stateFromChildren}
</p>
)}
<Children ref={ref} {...ChildrenProps} />
</div>
);
};
type ChildrenRefProps = {
getValueStateA: () => void;
};
type ChildrenProps = {
stateParent: string;
};
const Children = forwardRef<ChildrenRefProps, ChildrenProps>(
(props: any, ref) => {
const [stateA, setStateA] = useState('A');
useImperativeHandle(
ref,
() => {
return {
getValueStateA: () => {
return stateA;
},
};
},
[]
);
return (
<div>
<p>This is value of StateA: {stateA}</p>
<p>This is value of StateParent: {props?.stateParent}</p>
</div>
);
}
);
export default Parent;
So, that’s simple example of how to combine useImperativeHandle and useRef on the ReactJs. So, basically, with useImperativeHandle we can expose all functions or variables on the children to parent, but I guess it also can implement with siblings component ya, but you need more effort to wrap it and make it shareable, rather than use that, I think we can use different approcah ya, for example using state management like context or state management like redux and etc.
So, by default to use useImperativeHandle we have to remember about this format
useImperativeHandler(nameRef, () ⇒ {
// expose functions/varaibles
return {
....
}
}, [dependencies])
Here, several tips when the right time to use useImperativeHandle?
- When the parent component needs to interact with the child component’s methods or properties.
- When the child component has complex internals that you don’t want to directly expose.
- When creating reusable components with a controlled API for ref-based interactions.
Also, please keep in mind, please don’t overusing this feature because it can lead to tightly coupled components, making them harder to manage or test. Use it sparingly and only when absolutely necessary.
Oke, I think that’s all about tutorial how to use useImperativeHandle to expose functions/variables from children to parent.
I hope this tutorial is useful and make an impact of your knowledge of this feature. I really appreciate yourself because you want to spend your time to write my article. Feel free to give me feedback to make it better and better for my posts.
Here for the completed code with useImperativeHandle.
https://stackblitz.com/edit/vitejs-vite-6abwb7px