Main.tsx:
import React from "react";
import { useRectangleControls } from "src/useRectangleControls";
export function Main() {
const rectangleRef = React.useRef<HTMLDivElement>(null);
const mainControls = useRectangleControls(rectangleRef);
React.useEffect(() => {
console.log(mainControls.isMouseInRectangle);
}, [mainControls.isMouseInRectangle]);
return (
<div className="w-full h-screen flex items-center justify-center">
<div className="w-[60%] h-[30%] bg-green-900" ref={rectangleRef} onMouseEnter={mainControls.onMouseEnter} onMouseLeave={mainControls.onMouseLeave}></div>
</div>
);
}
The above component returns a rectangle. The color of the rectangle depends on the mouse position. The event handlers for onMouseEnter
and onMouseLeave
were shifted to a custom hook called useRectangleControls:
import React from "react";
export function useRectangleControls(rectangleRef: React.RefObject<HTMLDivElement>) {
const [isMouseInRectangle, setIsMouseInRectangle] = React.useState(false);
function onMouseEnter() {
if (rectangleRef.current) {
setIsMouseInRectangle(true);
rectangleRef.current.classList.remove("bg-green-900");
rectangleRef.current.classList.add("bg-red-900");
}
}
function onMouseLeave() {
if (rectangleRef.current) {
setIsMouseInRectangle(false);
rectangleRef.current.classList.remove("bg-red-900");
rectangleRef.current.classList.add("bg-green-900");
}
}
return { onMouseEnter, onMouseLeave, isMouseInRectangle };
}
I want to collect all functions and return them in one variable. I want the return statement to look something like this: return {rectangleControls, isMouseInRectangle}
where rectangleControls
contains all functions inside the useRectangleControls
custom hook.
My goal is to avoid updating the return statement each time I add a new function to useRectangleControls
You can do that in the following way
import React from "react";
export interface Indexable<T = any> {
[key: string]: T|undefined;
}
export interface RectangleControls {
onMouseEnter: () => void;
onMouseLeave: () => void;
}
export function useRectangleControls(rectangleRef: React.RefObject<HTMLDivElement>) {
const [isMouseInRectangle, setIsMouseInRectangle] = React.useState(false);
// const rectangleControls: Indexable = {}; // one approach for typescript
const rectangleControls: RectangleControls = {
onMouseEnter: () => {},
onMouseLeave: () => {}
}; // second approach for typescript
rectangleControls.onMouseEnter = () => {
if (rectangleRef.current) {
setIsMouseInRectangle(true);
rectangleRef.current.classList.remove("bg-green-900");
rectangleRef.current.classList.add("bg-red-900");
}
}
rectangleControls.onMouseLeave = () => {
if (rectangleRef.current) {
setIsMouseInRectangle(false);
rectangleRef.current.classList.remove("bg-red-900");
rectangleRef.current.classList.add("bg-green-900");
}
}
return { rectangleControls, isMouseInRectangle };
}
Edit: Explaining the changes made in the hook. Here I have created an Object rectangleControls and converted the onMouseEnter, onMouseLeave to arrow function and assign them as key to rectangleControls, this will help you achieve what you are looking for exporting a single variable.