To reach this goal we have to be aware about two different functions in react, useImperativeHandle
and forwardRef
.
I will not explain what are this function individually, it is better if we see it as a group.
When we use useImperativeHandle
, the code will work until you use forwarRef
in the same component, you will see an error like this if you are not using it.
To have better explanation of how to do it, let's do some code.
Imagine you need a button and this button have to save the amount of clicks that the user presses but we have to access this information only with another button in the same page, so basically we will have something like this.
First let's create the children component
import { forwardRef, useImperativeHandle, useState } from "react";
import PropTypes from "prop-types";
const CustomButton = forwardRef(
({ className, action = () => {}, children }, ref) => {
const [count, setCount] = useState(0);
// we are returning a function inside the ref
// to returning the count of clicks
useImperativeHandle(ref, () => ({
getCount() {
return count;
},
}));
return (
<button
className={`button ${className}`}
onClick={() => {
setCount((count+= 1)); // we count the clicks
action();
}}
>
{children}
</button>
);
}
);
// This line is because we get a warning
// When we use forwardRef we always have to have displayName
CustomButton.displayName = "Custom Button";
CustomButton.propTypes = {
className: PropTypes.string.isRequired,
action: PropTypes.func.isRequired,
};
export default CustomButton;
If you notice in the useImperativeHandle
function we are returning a function call getCount()
which returns the amount of clicks, but now maybe you are wondering how to use this component. Let's create the parent component.
import { useRef } from "react";
import CustomButton from "../components/customButton";
export default function Example() {
// we create a reference to the first button
const button1 = useRef();
// function of the first button
const onClick = () => {
console.log("do some action");
};
// function of the second button
const onClick2 = () => {
// we get the count with the reference of the first button
console.log(ref.current.getCount());
};
return (
<>
<CustomButton ref={button1} action={onClick} className="is-success">
Button 1
</CustomButton>
<CustomButton action={onClick2} className="is-danger mt-3">
Get count of button 1
</CustomButton>
</>
);
}
As you can see, the function of the second button gets the count of the first button, but let run it and do some clicks and see the console.
The console says we have press the button 9 times, now let's press the second button.
We get the amount successfully! But let's do some more to see if we still get the right answer.
Conclusion
We can return anything from our children components, like a function as we did, or objects, string, and if you want we can return elements of the DOM or even another references, the power of this is pretty amazing.
There is another approach of passing data from a children to a parent but this is for another post...
Thank you so much for reading and feel free to contact me if you need anything.
Top comments (1)
Well explained and it works everything correctly, nice keep doing great!