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

时间:2020-09-18 19:37:00

标签: reactjs react-hooks eslint

我正在尝试创建一个方便的反应挂钩,以使其内容适合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, y, width, 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中。

1 个答案:

答案 0 :(得分:1)

好的,我找到了一个受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, y, width, height].join(' '));
  }, []);

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

  return [svg, viewBox];
};