为什么修改useState中存储的数组时会同步改动原始数组?
问题根因
- 你使用的
[...genreOptions]是浅拷贝,仅复制了外层数组的引用,内部存储的每一个[流派名, 布尔值]子数组仍然和原始genreOptions共享引用,修改子数组的值会同步影响原始数组。 - 代码中
g[(i, 1)]的写法存在语法误区:逗号表达式会返回最后一个值,这里等价于g[1],虽然当前场景下结果符合预期,但属于错误写法,建议直接写g[1]。 - 你直接修改了状态引用指向的子数组内容(
g[1] = !g[1]),属于直接变更React状态,不符合React不可变更新的规范。 - 全局声明的
genreOptClone只会初始化一次,在组件运行过程中已经被修改,无法作为初始值的备份使用。
修复方案
- 初始化状态时直接从原始数组做双层拷贝,保证和原始数组完全隔离
- 点击事件触发时,按照不可变更新的规则生成新的数组,不修改原有引用
- 修正语法错误,删除不必要的逗号表达式
修复后代码
const genreOptions = [ ["rock", true], ["pop", true], ["punk", true], ["acoustic", true], ["alternative", true], ["ambient", true], ["blues", true], ["classical", true], ["chill", true], ["comedy", true], ["country", true], ["dance", true], ["disco", true], ["electronic", true], ["folk", true], ["drum and bass", true], ["dub", true], ["funk", true], ["gospel", true], ["garage", true], ["hip hop", true], ["house", true], ["indie", true], ["jazz", true], ["metal", true], ["new age", true], ["opera", true], ["r&b", true], ["reggae", true], ["rock and roll", true], ["ska", true], ["soul", true], ["soundtracks", true], ["techno", true], ["world music", true], ]; function ShuffleControls({ nextTrack, currentTrack, token: receivedToken }) { // 初始化时直接从原始数组做双层拷贝,完全隔离引用 const [genres, setGenres] = useState(() => genreOptions.map(item => [...item]).sort() ); const handleGenreClick = (i) => { // 不可变更新:生成全新的数组,不修改原有状态引用 setGenres(prev => { const newGenres = [...prev]; newGenres[i] = [...newGenres[i]]; newGenres[i][1] = !newGenres[i][1]; return newGenres; }); }; return ( <div> {genres.map((g, i) => ( <button type="button" key={i} onClick={() => { handleGenreClick(i); console.log("genreOptions onClick: ", genreOptions); }} className={g[1] ? "genres-btn" : "genres-btn btn-disabled"} > {g[0]} </button> ))} </div> ); } export default ShuffleControls;
额外说明
如果需要重置状态,直接调用setGenres(genreOptions.map(item => [...item]).sort())即可,不会影响原始genreOptions的值。
内容的提问来源于stack exchange,提问作者JoshIsHere
相关产品推荐
相关产品推荐

