React Redux异常:Reducer数组拷贝为空且状态未更新问题咨询
嘿,这种诡异的情况我之前也遇到过,咱们来一步步排查解决:
首先先把你的reducer代码格式化一下,方便更清晰地查看:
const pokemons = (state = [], action) => { switch(action.type){ case types.FETCH_POKEMON: var a = action.pokemons console.log(a) var b = [...action.pokemons] console.log(b) return a; default: return [...state]; } }
核心问题分析
你遇到的两个现象(b为空数组、state不更新)大概率是同一个原因导致的:
action.pokemons不是标准数组
看起来它是数组,但实际可能是类数组对象(比如API返回的响应被解析成了带有数字索引但没有数组原型的对象,或者是NodeList这类集合)。扩展运算符[...]只对实现了Symbol.iterator接口的可迭代对象生效,如果action.pokemons没有这个接口,就会生成空数组。另外还有一个容易踩的坑:
console.log会延迟解析引用类型——你看到的a的内容可能是后续被修改后的结果,实际执行var b = [...action.pokemons]的时候,action.pokemons其实是空的。Redux的state更新依赖新引用
就算a有值,如果a和原来的state是同一个数组引用,Redux的浅比较会认为state没有变化,不会触发组件更新。你直接返回a而不是新数组的话,就可能出现这种情况。
验证与解决方法
第一步:先确认action.pokemons的真实类型
在console.log(a)前加一行验证代码,搞清楚它到底是不是标准数组:
console.log("类型检查:", typeof action.pokemons, Array.isArray(action.pokemons), action.pokemons);
如果Array.isArray(action.pokemons)返回false,说明它是类数组对象,这时候换用这些方法转成标准数组:
- 方法一:用
Array.from()(ES6+推荐写法)var b = Array.from(action.pokemons); - 方法二:用
slice()兼容旧环境var b = Array.prototype.slice.call(action.pokemons);
第二步:确保返回新的state引用
不管action.pokemons是不是数组,都不要直接返回a,而是返回一个新数组,这样Redux才能检测到state变化:
return [...a]; // 或者用 Array.from(a)
额外排查:确认action的dispatch时机
如果上面的方法都没用,检查一下你dispatch这个action的时候,action.pokemons是不是真的已经拿到了数据——比如API请求还在pending状态就dispatch了,导致action.pokemons是空数组,后续数据回来后才填充,但reducer已经执行过了。这种情况要确保在请求完成、数据完全拿到后再dispatch action。
内容的提问来源于stack exchange,提问作者Huong Nguyen

