React Native Stack导航切屏时GameScreen State未正确重置
异常根因定位
require('../data/challenges.json')在React Native环境中是模块系统托管的全局缓存单例,每次调用不会生成新的JSON副本,所有引用指向同一块内存中的原始对象。- 首次进入游戏界面时,你在
playChallenge、fillInChallenge流程中直接对这个缓存单例的对象属性做原地修改:把原本带p(1)、sips(2,4)占位符的原始文本,永久替换成了随机填充的实际值。 - 二次进入游戏界面时,
useState初始化逻辑再次执行require读取,拿到的还是第一次被修改过的缓存对象,自然不会再出现未填充的原始占位符文本。 - 代码中多处共享引用的操作加剧了这个问题:
- 如果
shuffle工具函数内部直接对入参数组做原地乱序,会直接修改传入的缓存数组结构 playChallenge中直接给currentChallenges[0]重新赋值、对数组做splice插入,没有做拷贝隔离popChallengeHandler中的[...challenges]只是数组浅拷贝,数组内的对象元素依然和全局JSON缓存共享引用,修改时依然会污染源对象- 当前
fillInChallenge实现存在明显疏漏:没有把待处理的挑战对象作为入参传入,仅把players参数传给了fillInChoice,填充逻辑没有和当前挑战的文本内容绑定
- 如果
修复方案
- 读取JSON数据源后第一时间生成独立深拷贝副本,彻底切断和模块缓存对象的引用关联:
const [challenges, setChallenges] = useState(() => { // 读取模块缓存的原始JSON const rawDeck = require('../data/challenges.json').deck; // 深拷贝生成全新的操作副本,后续所有操作都不会影响原缓存 const copiedDeck = JSON.parse(JSON.stringify(rawDeck)); // 所有过滤、乱序、填充操作都在copiedDeck上执行 const filteredDeck = copiedDeck.filter(challenge => players.length >= playersNeeded(challenge)); return playChallenge(shuffle(filteredDeck)); }); - 修正
shuffle工具函数的实现逻辑,确保乱序操作不会修改入参原数组:// 修正后的shuffle示例 export const shuffle = (sourceArr) => { // 先拷贝一层数组,避免直接修改原数组 const arr = [...sourceArr]; for (let i = arr.length - 1; i > 0; i--) { const randomIndex = Math.floor(Math.random() * (i + 1)); [arr[i], arr[randomIndex]] = [arr[randomIndex], arr[i]]; } return arr; } - 修正
fillInChallenge的入参逻辑,确保填充操作在独立对象副本上执行,不直接修改原对象:const fillInChallenge = (challenge) => { // 先创建挑战对象的浅拷贝 const copiedChallenge = {...challenge}; // 把当前挑战的文本内容、玩家列表传给替换函数完成占位符替换 const filledText = fillInChoice(copiedChallenge.initialRound, players); // 把填充后的文本写回拷贝对象,不影响原缓存 copiedChallenge.initialRound = filledText; return copiedChallenge; } - 调整
playChallenge逻辑,确保所有数组操作都是基于新数组执行,不做原地修改:const playChallenge = (currentChallenges) => { const [firstChallenge, ...restChallenges] = currentChallenges; const processedFirst = fillInChallenge(firstChallenge); // 把下一轮的挑战插入到队列里,生成全新的数组 const newQueue = [processedFirst, ...restChallenges]; processedFirst.nextRounds.forEach(round => { newQueue.splice(round[0], 0, { initialRound: round[1], nextRounds: [] }) }) return newQueue; }
内容的提问来源于stack exchange,提问作者P200p
相关产品推荐
相关产品推荐

