The Idea
I had a function that finds an elements center y and center x coordinates. It seemed like something that could be useful in many places, so I converted it into a hook.
The Process
The easy first step was to cut the function itself and paste it into a new file.
Type issues. The return value and ref argument would require a div element. What if I want to use something else?
One challenge with that was getting the proper type for the ref element. I could require divs, but what if I want to use another element? I'm still pretty new to TypeScript, and haven't quite wrapped my head around things like utilizing generics.
Took a bit of searching but finally found this on Stack Overflow Something like this does the trick!
// The original function from my component. function getElementCenter({ offsetTop, offsetLeft, clientWidth, clientHeight }: CenterArgs) { const centerX = offsetLeft + clientWidth / 2; const centerY = offsetTop + clientHeight / 2; return { centerX, centerY }; } type UseElementCenterType<T extends HTMLElement> = [number, number, RefObject<T>] export function useElementCenter<T extends HTMLElement>(): UseElementCenterType<T> { const centerRef = useRef<T>(null); const [centerX, setCenterX] = useState(0); const [centerY, setCenterY] = useState(0); return [ centerX, centerY, centerRef ] }
Then whenever you use the hook, you explicitly declare what sort of element you're passing through the ref.
const [centerX, centerY, centerRef] = useElementCenter<HTMLDivElement>();
Well, that works... almost. Turns out that using this as a hook causes a render error due to the ref initializing as null. If there's no element, there are no dimensions to pass to the getElementCenter
function. I solved that using a useEffect()
hook to only call the function if there's an element being ref'd.
// Need useEffect here to account for initiating the ref as null. useEffect(() => { if(centerRef.current) { const { offsetTop, offsetLeft, clientWidth, clientHeight, } = centerRef.current; const { centerX, centerY } = getElementCenter({ offsetTop, offsetLeft, clientWidth, clientHeight }); setCenterX(centerX); setCenterY(centerY); } });
useRef(null) issue solved by useEffect