I'm trying to create a handy react hook to fit an SVG's viewBox
to it's content.
import { useState } from 'react';
import { useEffect } from 'react';
import { useRef } from 'react';
// get fitted view box of svg
export const useViewBox = () => {
const svg = useRef();
const [viewBox, setViewBox] = useState(undefined);
useEffect(() => {
// if svg not mounted yet, exit
if (!svg.current)
return;
// get bbox of content in svg
const { x, y, width, height } = svg.current.getBBox();
// set view box to bbox, essentially fitting view to content
setViewBox([x, y, width, height].join(' '));
});
return [svg, viewBox];
};
then using it:
const [svg, viewBox] = useViewBox();
return <svg ref={svg} viewBox={viewBox}>... content ...</svg>
But I get the following eslint error:
React Hook useEffect contains a call to 'setViewBox'. Without a list of dependencies, this can lead to an infinite chain of updates. To fix this, pass [viewBox] as a second argument to the useEffect Hook.eslint(react-hooks/exhaustive-deps)
I've never run into a situation where the react hooks eslint errors were "wrong", until now. I feel like this is a perfectly legitimate use of hooks. It needs to run as an effect, because it needs to run AFTER the render to see if the contents of the SVG have changed. And as far as the warning message: this code already avoids an infinite render loop because setState doesn't fire a re-render unless the new value is different from the current one.
I can disable the eslint rule:
// eslint-disable-next-line react-hooks/exhaustive-deps
But that seems wrong, and I'm wondering if there's a simpler/different way to achieve the same goal that I'm not seeing.
I could have the caller of useViewBox
provide some variable that would go in useEffect
's dependency array and would force a re-render, but I want it to be more flexible and easier to use than that.
Or perhaps the problem actually lies in the exhaustive-deps
rule. Maybe it should allow setState
within a no-dependencies-specified useEffect
if it detects some conditionality in front of the setState
...