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

React嵌套数组重渲染问题:Quiz应用选项重复洗牌

问题解决:Quiz应用选项重复洗牌的问题

问题根源

你遇到的核心问题是:每次组件重渲染(比如用户选择选项时),question?.pairs.map(...)都会生成一个全新的数组,导致useShuffle Hook的依赖项(array)发生变化,触发useMemo重新执行,进而重新洗牌选项。而直接传入pairs数组时,只要原pairs未被修改,依赖项就不会变化,因此不会重复洗牌。

解决方案

方案1:提前缓存提取后的选项数组

在组件中先用useMemo缓存提取出的questionOption和answerOption数组,再传给useShuffle,确保只有原pairs变化时才重新生成这些数组:

// 在Question组件中
const questionOptions = useMemo(() => {
  return question?.pairs.map(pair => pair.questionOption) ?? [];
}, [question?.pairs]);

const answerOptions = useMemo(() => {
  return question?.pairs.map(pair => pair.answerOption) ?? [];
}, [question?.pairs]);

const shuffledLeftSideOptions = useShuffle(questionOptions);
const shuffledRightSideOptions = useShuffle(answerOptions);

方案2:修改useShuffle Hook,依赖原pairs数组

直接让useShuffle依赖原pairs数组,在内部完成选项提取和洗牌,避免外部传递每次都变化的数组:

// 修改为针对Connections类型的专用Hook
export const useShuffleConnectionsOptions = (question?: IConnectionsQuestionData) => {
  return useMemo(() => {
    if (!question) return { left: [], right: [] };
    const left = shuffleArray(question.pairs.map(pair => pair.questionOption));
    const right = shuffleArray(question.pairs.map(pair => pair.answerOption));
    return { left, right };
  }, [question?.pairs]);
};

// 在Question组件中使用
const { left: shuffledLeftSideOptions, right: shuffledRightSideOptions } = useShuffleConnectionsOptions(question);

额外注意:确保shuffleArray是纯函数

要保证shuffleArray不会修改原数组,避免引发意外状态变化,推荐使用Fisher-Yates洗牌算法的纯函数实现:

function shuffleArray<T>(array: T[]): T[] {
  // 创建原数组副本,避免修改原数据
  const newArray = [...array];
  for (let i = newArray.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [newArray[i], newArray[j]] = [newArray[j], newArray[i]];
  }
  return newArray;
}

修改后,只有原question.pairs数组发生变化时,选项才会重新洗牌,组件因用户操作重渲染时不会触发重复洗牌。

内容的提问来源于stack exchange,提问作者Антон Стецюра

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 01:32:01