如何在Immer的Produce中递归修改值?遇对象不可扩展错误
在Immer的produce函数中递归修改值的最佳实现方案
问题背景
需要在Immer的produce函数中通过递归调用修改嵌套的消息结构,当前实现触发以下错误:
modal.component.tsx:65 未捕获的类型错误:无法添加属性0,对象不可扩展,位于Array.push (
)
类型定义:
type WhatsappState = { message: Message, messages: Message[], nextMessage: Message, isAuth: boolean, qrCode: string, modalIsOpen: boolean } type Message = { value: string; id: number; nextMessage: Message[]; uid: string; messageState: 'create' | 'submessage' | 'edit'; }
当前尝试的代码:
const submessage = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => { const state = {...WhatsappState, teste: [new Message()]} console.table({message: state.message, nextMessage: state.nextMessage}) console.log(state) const newMessage = produce(state, draft => { const addMessage = (messages: Array<Message>, uid: string, msg: Message) => { for (const message of messages) { if (message.uid === uid){ console.table({message, msg}) if(!message.nextMessage) message.nextMessage = []; message.nextMessage.push(msg) return true; } else if(addMessage(createDraft(message.nextMessage), uid, msg)){ return true; } } return false } addMessage(draft.messages, state.nextMessage.uid, state.message) }) console.log(newMessage); WhatsappDispatch({ type: "set_state", payload: { value: newMessage } }) closeModal() }
问题分析
- 手动调用
createDraft属于冗余操作:Immer会自动将draft的嵌套对象转为可修改的代理,无需手动创建draft - 传入的
msg是原始状态中的冻结对象:Redux等状态管理库的状态通常不可扩展,直接push会触发“对象不可扩展”错误 - 不必要的state复制:
produce会基于传入的原始状态创建draft,无需手动浅拷贝state
修正后的实现
const submessage = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => { const newMessage = produce(WhatsappState, draft => { const addMessage = (messages: Array<Message>, targetUid: string, newMsg: Message) => { for (const message of messages) { if (message.uid === targetUid) { // 确保nextMessage数组存在 if (!message.nextMessage) { message.nextMessage = []; } // 浅拷贝新消息,避免引用冻结的原始对象 message.nextMessage.push({...newMsg}); return true; } // 仅当nextMessage存在时递归,避免传入undefined else if (message.nextMessage && addMessage(message.nextMessage, targetUid, newMsg)) { return true; } } return false; } // 操作draft中的数据,而非原始状态 addMessage(draft.messages, draft.nextMessage.uid, {...draft.message}); }); WhatsappDispatch({ type: "set_state", payload: { value: newMessage } }); closeModal(); };
关键优化点
- 直接基于原始
WhatsappState创建draft,省去手动拷贝步骤 - 递归时直接传递draft中的
message.nextMessage,依赖Immer自动处理代理 - 对新消息做浅拷贝
{...draft.message},确保push的是可扩展的新对象 - 增加
message.nextMessage的存在性检查,避免递归调用时传入undefined
内容的提问来源于stack exchange,提问作者Gilmar Antonio Ribeiro dos San
相关产品推荐
相关产品推荐

