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

React useReducer处理函数为何是不纯函数?StrictMode交换异常排查

React StrictMode下useReducer两次调用导致交换异常的问题解决

你的问题核心是reducer函数不是纯函数,浅拷贝后直接修改了原状态的数组引用,导致StrictMode两次调用时,第二次的状态已经被第一次修改,最终触发两次交换,让元素跳过一个位置。

问题代码的关键缺陷

let modified_song = {...song}
// ...后续直接修改modified_song.song.songsheet.aSections的元素

{...song}只是浅拷贝,modified_song.song.songsheet.aSections仍然和原状态的数组指向同一个引用。当你修改这个数组的元素时,实际上是在直接修改原状态中的数组——这违反了reducer必须是纯函数的要求:纯函数不能修改输入参数,必须返回全新的状态,且相同输入必须得到相同输出。

在StrictMode的开发环境下,React会重复调用reducer来检测不纯的副作用。第一次调用时,原数组已经被修改;第二次调用时,reducer拿到的song已经是第一次交换后的状态,相当于连续执行了两次交换操作,自然会让元素跳过一个位置(比如原位置i,第一次和i+1交换,第二次又和i+2交换)。

修复方案:创建全新的数组副本

要保证reducer的纯函数特性,必须对aSections创建新的数组副本,在新数组上完成交换操作,而不是修改原引用的数组。修改后的代码如下:

case 'swap_section': {
  // 逐层拷贝到aSections层级,确保每个引用都是新的
  const modified_song = {
    ...song,
    song: {
      ...song.song,
      songsheet: {
        ...song.song.songsheet,
        aSections: [...song.song.songsheet.aSections] // 创建数组的新副本
      }
    }
  };

  const target_section_index = modified_song.song.songsheet.aSections.findIndex(
    section => action.daw === section.daw
  );
  const swap_section_index = action.direction === "down" 
    ? target_section_index + 1 
    : target_section_index - 1;

  // 检查交换索引的有效性
  if (swap_section_index > -1 && swap_section_index < modified_song.song.songsheet.aSections.length) {
    // 在新数组上执行交换
    const temp = modified_song.song.songsheet.aSections[target_section_index];
    modified_song.song.songsheet.aSections[target_section_index] = modified_song.song.songsheet.aSections[swap_section_index];
    modified_song.song.songsheet.aSections[swap_section_index] = temp;
  }

  return modified_song;
}

修复逻辑说明

  • 通过逐层拷贝,我们创建了aSections的全新数组副本,修改这个新数组不会影响原状态,确保每次调用reducer时,输入的song都是未被修改的初始状态。
  • 即使StrictMode两次调用reducer,两次执行都是基于相同的初始状态,最终React会忽略第二次的执行结果,只保留第一次的有效交换,不会出现元素跳过的问题。

额外提醒

reducer必须始终是纯函数:

  • 不能修改传入的state参数
  • 不能执行有副作用的操作(比如异步请求、DOM修改、Math.random()等)
  • 相同的输入必须返回完全相同的输出

这样才能保证React状态的可预测性,同时让StrictMode的检测机制正常工作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 05:22:39