You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js中Wilayas组件添加事件监听器时重复渲染问题求助

问题:Next.js旅游应用Wilayas组件点击事件重复触发导致卡顿

我正在用Next.js开发一款旅游应用,在Wilayas组件中遇到异常:组件内通过fetch加载SVG地图,通过useEffect为地图每个区域添加点击事件监听器,触发的滑动动画可正常运行。但控制台中调试用的console.log("hello")输出次数随点击次数翻倍递增:点击1次输出1次,点击2次输出3次,点击4次输出15次,最终导致页面卡顿。

Destinations页面直接在代码中内嵌SVG地图,通过React的onClick属性添加相同的事件处理函数,运行完全正常。但由于各区域的SVG地图多达11份,无法全部内嵌,因此需要解决该问题。

Wilayas组件代码

const Wilayas = ({ region }: { region: any }) => {
  const [wilayas, setWilayas] = useState<HTMLElement[]>([]);
  const [currentIndex, setCurrentIndex] = useState(0);
  const [left, setLeft] = useState(0);
  const [back, setBack] = useState(false);
  const [forward, setForward] = useState(true);
  const sectionRef = useRef<HTMLUListElement>(null);

  const goBack = () => {
    setCurrentIndex(currentIndex - 1);
    if (sectionRef.current) {
      sectionRef.current.style.transform = `translate(${left + 27.278566666666666}vw)`;
    }

    const updatedWilayas = [...wilayas];
    updatedWilayas.forEach((wilaya) => wilaya?.classList.add("lighten"));
    updatedWilayas[currentIndex]?.classList.remove("darken");
    updatedWilayas[currentIndex - 1]?.classList.add("darken");
    updatedWilayas[currentIndex - 1]?.classList.remove("lighten");
    setWilayas(() => updatedWilayas);

    setLeft(left + 27.278566666666666);
    if (currentIndex === 1) {
      setBack(false);
    } else {
      setForward(true);
    }
  };

  const goForward = () => {
    setCurrentIndex(currentIndex + 1);
    if (sectionRef.current) {
      sectionRef.current.style.transform = `translate(${left - 27.278566666666666}vw)`;
    }

    const updatedWilayas = [...wilayas];
    updatedWilayas.forEach((wilaya) => wilaya.classList.add("lighten"));
    updatedWilayas[currentIndex]?.classList.remove("darken");
    updatedWilayas[currentIndex + 1]?.classList.add("darken");
    updatedWilayas[currentIndex + 1]?.classList.remove("lighten");
    setWilayas(() => updatedWilayas);

    setLeft(left - 27.278566666666666);
    if (currentIndex === 8) {
      setForward(false);
    } else {
      setBack(true);
    }
  };

  const goToExactRegion = useCallback(
    (index: number) => {
      setCurrentIndex(index);
      const updatedWilayas = [...wilayas];
      console.log("hello");

      updatedWilayas.forEach((wilaya) => wilaya.classList.add("lighten"));
      updatedWilayas[currentIndex]?.classList.remove("darken");
      updatedWilayas[index]?.classList.add("darken");
      updatedWilayas[index]?.classList.remove("lighten");

      if (index < currentIndex) {
        sectionRef.current!.style.transform = `translate(${left + (currentIndex - index) * 27.278566666666666}vw)`;
        if (index === 0) {
          setBack(false);
        }
        setForward(true);
        setLeft(left + (currentIndex - index) * 27.278566666666666);
      } else if (index > currentIndex) {
        sectionRef.current!.style.transform = `translate(${left - (index - currentIndex) * 27.278566666666666}vw)`;
        if (index === 9) {
          setForward(false);
        }
        setBack(true);
        setLeft(left - (index - currentIndex) * 27.278566666666666);
      }

      setWilayas(() => updatedWilayas);
    },
    [wilayas]
  );

  const handleSwitch = (index: number) => () => {
    goToExactRegion(index);
  };

  useEffect(() => {
    if (document.getElementById("region") !== null) {
      const fetching = async () => {
        const response = await fetch(`https://ik.imagekit.io/vaqzdpz5y/assets/images/${region.id}/3.svg`);
        const svgContent = await response.text();
        console.log("hello");
        document.getElementById("region")!.innerHTML = svgContent;
        document.getElementById("svg2")!.style.width = "100%";
        const wilayat = [];
        for (let i = 1; i < 11; i++) {
          const wilaya = document.getElementById(`${i}`);
          if (wilaya) wilayat.push(wilaya);
        }
        setWilayas(wilayat);
      };
      fetching();
    }
  }, []);

  useEffect(() => {
    const wilayat: HTMLElement[] = [];
    for (let i = 1; i < 11; i++) {
      const wilaya = document.getElementById(`${i}`);
      if (wilaya) {
        wilaya.addEventListener("click", handleSwitch(i - 1));
        wilayat.push(wilaya);
      }
    }

    return () => {
      wilayat.forEach((wilaya, i) => {
        wilaya.removeEventListener("click", handleSwitch(i - 1));
      });
    };
  }, [wilayas, handleSwitch]);
return (
<div>
   ...
   <div id="region" className="w-[51.5625vw]"></div>
</div>
)

注:最后一个div用于加载SVG地图。

SVG代码说明

仅需关注每个path元素的ID,示例代码如下:

<svg version="1.1" id="svg2" x="0px" y="0px" viewBox="0 0 207.52948 125.99589"
  enable-background="new 0 0 982.451 955.452"
  <defs id="defs63" />

  <path id="1" fill="#03c988" stroke="#ffffff" stroke-width="0.75" stroke-linecap="round" d="m ..." />

  <path id="9" fill="#03c988" stroke="#ffffff" stroke-width="0.75" stroke-linecap="round" d="m ..." />

  <path id="8" fill="#03c988" stroke="#ffffff" stroke-width="0.75" stroke-linecap="round" d="m ..." />

  <path id="5" fill="#03c988" stroke="#ffffff" stroke-width="0.75" stroke-linecap="round" d="m ..." />

  <path id="4" fill="#03c988" stroke="#ffffff" stroke-width="0.75" stroke-linecap="round" d="m ..." />

  <path id="6" fill="#03c988" stroke="#ffffff" stroke-width="0.75" stroke-linecap="round" d="m ..." />

  ...

  <path id="3" fill="#03c988" stroke="#ffffff" stroke-width="0.75" stroke-linecap="round" d="m ..." />

  <path id="10" fill="#03c988" stroke="#ffffff" stroke-width="0.75" stroke-linecap="round" d="m ..." />

  <path id="2" fill="#03c988" stroke="#ffffff" stroke-width="0.75" stroke-linecap="round" d="m ..." />

  <path id="7" fill="#03c988" stroke="#ffffff" stroke-width="0.75" stroke-linecap="round" d="m ..." />

  ...
</svg>

内容的提问来源于stack exchange,提问作者ashendhiaa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 23:25:00