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

Next.js中setState回调内Server Action被调用两次的解决方法

解决Next.js StrictMode导致Server Action重复调用的问题

问题原因

在Next.js的StrictMode开发环境中,React会重复执行useState的更新器函数(即setNumLemons的回调),以此验证该函数是否为纯函数(无副作用)。你把后端请求serverActionUpdateLemons放在了这个更新器函数里,导致它被调用两次。核心问题在于:useState更新器函数必须是纯函数,不能包含后端请求这类副作用操作。


解决方案

方案1:分离状态更新与副作用(推荐)

将状态计算(纯逻辑)和后端请求(副作用)分开,符合React设计原则,同时避免重复调用。

const [numLemons, setNumLemons] = useState(initial_array);

function incrementHandler(user_id) {
  // 纯函数逻辑:计算新状态值
  const updatedLemons = numLemons.map(item => 
    item.id === user_id 
      ? { ...item, lemons_count: item.lemons_count + 1 } 
      : item
  );

  // 更新组件状态
  setNumLemons(updatedLemons);

  // 调用后端接口(仅执行一次)
  const newCount = updatedLemons.find(item => item.id === user_id).lemons_count;
  serverActionUpdateLemons(user_id, newCount);
}

说明:事件处理函数incrementHandler在StrictMode下不会被重复执行,因此后端请求只会触发一次。状态计算逻辑保持纯函数,完全符合React的要求。

方案2:用useRef标记避免重复执行(临时方案)

如果因业务限制必须在更新器函数中调用后端接口,可以用useRef跟踪执行状态,跳过StrictMode的第二次调用:

const [numLemons, setNumLemons] = useState(initial_array);
const isProcessing = useRef(false);

function incrementHandler(user_id) {
  setNumLemons(prev => {
    // 第二次调用时直接返回原状态,不执行副作用
    if (isProcessing.current) {
      isProcessing.current = false;
      return prev;
    }

    const tempArray = [...prev];
    const changeIndex = tempArray.findIndex(e => e.id === user_id);
    const newCount = tempArray[changeIndex].lemons_count + 1;
    
    tempArray[changeIndex] = { ...tempArray[changeIndex], lemons_count: newCount };
    
    serverActionUpdateLemons(user_id, newCount);
    isProcessing.current = true;
    
    return tempArray;
  });
}

说明:StrictMode下更新器函数会连续调用两次,useRef的current值在两次调用间保持不变,第一次调用时执行请求并标记为已处理,第二次调用时直接返回原状态。注意这种方式会破坏更新器函数的纯函数特性,仅作为临时 workaround 使用。

方案3:关闭StrictMode(不推荐)

如果只是开发环境临时测试,可以关闭StrictMode,但不建议长期使用——StrictMode能帮助提前发现潜在的副作用问题,对代码质量有重要作用。

在Next.js 13+的app/layout.js中,移除<StrictMode>包裹:

// app/layout.js
export default function RootLayout({ children }) {
  return (
    <html lang="zh-CN">
      <body>{children}</body>
    </html>
  );
}

说明:生产环境下StrictMode默认关闭,线上不会出现重复调用问题,但开发环境会失去StrictMode的校验能力。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 10:42:50