对ESGint规则失败的SVG viewBox做出反应“ useViewBox”挂钩

问题描述

我正在尝试创建一个方便的反应挂钩,以使其内容适合SVG的viewBox

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,height].join(' '));
  });

  return [svg,viewBox];
};

然后使用它:

const [svg,viewBox] = useViewBox();

return <svg ref={svg} viewBox={viewBox}>... content ...</svg>

但是我收到以下eslint错误

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)

到目前为止,我从来没有遇到过React hooks eslint错误为“错误”的情况。我觉得这是对钩子的完全合法使用。它需要作为一种效果运行,因为它需要在渲染之后运行,以查看SVG的内容是否已更改。至于警告消息:此代码已经避免了无限渲染循环,因为除非新值与当前值不同,否则setState不会触发重新渲染。

我可以禁用eslint规则:

// eslint-disable-next-line react-hooks/exhaustive-deps

但这似乎是错误的,我想知道是否有一种更简单/不同的方法来实现我没有看到的相同目标。

我可以让useViewBox调用者提供一些变量,该变量将进入useEffect的依赖项数组中并强制重新渲染,但我希望它更灵活,更易于使用比那更

或者问题可能出在exhaustive-deps规则上。如果它在setState前面检测到某些条件,则应该允许useEffect在无依赖项指定的setState中。

解决方法

好的,我找到了一个受this answer启发的“解决方案”。我认为这是一种愚蠢的解决方法,但我认为它比禁用eslint规则要好:

import { useState } from 'react';
import { useEffect } from 'react';
import { useRef } from 'react';
import { useCallback } from 'react';

// get fitted view box of svg
export const useViewBox = () => {
  const svg = useRef();
  const [viewBox,setViewBox] = useState(undefined);

  const getViewBox = useCallback(() => {
    // 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,height].join(' '));
  },[]);

  useEffect(() => {
    getViewBox();
  });

  return [svg,viewBox];
};