From b03bfe9c17096197f75d217208d0e3668163ae7f Mon Sep 17 00:00:00 2001 From: Zhao Jianing <18910855655@163.com> Date: Thu, 22 Oct 2026 22:08:38 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E8=BD=AE=E6=92=AD=E8=AE=A1=E6=97=B6?= =?UTF-8?q?=E5=99=A8=E4=B8=8D=E5=9C=A8=20setState=20updater=20=E5=86=85?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E7=88=B6=E7=BB=84=E4=BB=B6=E5=9B=9E=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit setProgress 的 updater 函数内嵌 setActiveFun()(父 state 更新)—— React 要求 updater 必须是纯函数:StrictMode 与中断/重放的并发渲染 可能重复调用 updater,导致轮播跳标签双触发(当前被 next.config.js 的 reactStrictMode: false 和幂等的 next-name 计算掩盖,任何未来 改为索引推进的修改都会双跳)。 改为纯 updater(推进/归零)+ 独立 effect 在 progress >= 101 时 调用 activeFunRef.current()。setActiveFun 存 ref:父组件传的 inline 闭包每次渲染都是新引用,放在 effect deps 里导致 interval 频繁重建。 --- .../modules/academe/CooperationCase/Timer.tsx | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/apps/web/src/modules/academe/CooperationCase/Timer.tsx b/apps/web/src/modules/academe/CooperationCase/Timer.tsx index 594d1eb826..7eaf2fcf7a 100644 --- a/apps/web/src/modules/academe/CooperationCase/Timer.tsx +++ b/apps/web/src/modules/academe/CooperationCase/Timer.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import { Progress } from 'antd'; const Timer = ({ @@ -12,22 +12,30 @@ const Timer = ({ }) => { const [progress, setProgress] = useState(0); + // setActiveFun 存 ref:父组件传的 inline 闭包每次渲染都是新引用, + // 放在 effect deps 里导致 interval 频繁重建。 + const activeFunRef = useRef(setActiveFun); + activeFunRef.current = setActiveFun; + useEffect(() => { const interval = setInterval(() => { !stop && - setProgress((prevProgress) => { - if (prevProgress >= 101) { - setActiveFun(); - return 0; - } else { - return prevProgress + 10 / time; - } - }); + setProgress((prevProgress) => + prevProgress >= 101 ? 0 : prevProgress + 10 / time + ); }, 100); return () => { clearInterval(interval); }; - }, [stop, time, setActiveFun]); + }, [stop, time]); + + // 到达终点在 effect 中推进(updater 必须是纯函数——React 在 StrictMode + // 与中断渲染下可能重复调用 updater,其中的 setActiveFun 会双触发)。 + useEffect(() => { + if (progress >= 101) { + activeFunRef.current(); + } + }, [progress]); return (