在Immer的setter内调用sort()报错:Uncaught TypeError: 0 is read-only
问题原因及解决办法
首先要明确:Array.sort()是原地排序方法——它会直接修改调用它的原数组,而非返回一个新的排序后数组。
你碰到的报错,核心是在useImmer的setState回调(也就是immer的produce函数)内部对result执行原地修改时,触发了只读限制,具体分两种场景:
- 如果
result是来自React状态的数组(比如某个useState/useImmer的状态值),React在开发模式下会自动冻结状态对象,防止直接修改原状态。这时候在produce回调里调用result.sort(),试图修改被冻结的数组,自然会抛出“0 is read-only”的错误。 - 哪怕
result是新创建的普通数组,immer在produce回调内部的执行逻辑会对数组修改操作做特殊追踪,原地修改的行为可能和immer的代理机制冲突,进而触发只读错误。
至于把sort()移到回调外面能正常运行,是因为此时数组的修改操作脱离了immer的追踪范围,不会触发内部的只读限制——但这其实是不规范的写法,如果result是状态值,直接修改原数组会导致React状态意外变更,埋下bug隐患。
正确的解决方式
不管sort()放在哪里,都不要直接修改原数组,先复制出一个新数组再排序:
function update(result) { setState(draft => { // 用扩展运算符复制原数组,再对新数组排序 let sortedResult = [...result].sort(); draft.arr[0] = sortedResult; }) }
也可以用Array.from()实现数组复制:
let sortedResult = Array.from(result).sort();
这种写法既不会修改原数组,也能完美避开immer的限制,逻辑更安全可靠。
内容的提问来源于stack exchange,提问作者Chin
相关产品推荐
相关产品推荐

