forEach构造新对象时部分seller属性变为React元素问题
问题表现
- 从Moralis查询返回的原始对象数组中,
attributes.seller字段均为字符串类型,遍历过程中直接打印该字段,输出结果全部为正常字符串 - 遍历提取字段构造新的offer对象推入
createdArr数组后,绝大多数offer的seller字段值异常,变为React按钮元素对象,结构为{$$typeof: Symbol(react.element), type: 'button', key: null, ref: null, props: {…}, …},仅少部分对象保留正确字符串值 - 该逻辑此前运行正常,当前仅部分条目出现异常
相关代码片段
遍历逻辑:
createdResults.forEach((item) => { console.log(item.attributes.seller); const offer = { itemId: Number(item.attributes.itemId), token: item.attributes.token, amount: item.attributes.amount, price: item.attributes.price, seller: item.attributes.seller, }; createdArr.push(offer); })
完整查询逻辑:
const getEvents = async () => { let createdArr = []; const created = new Moralis.Query("SaleCreateddd"); const createdResults = await created.find(); createdResults.map((item) => { // 此处打印item.attributes.seller全部显示为正常字符串 console.log(item.attributes.seller); const offer = { itemId: Number(item.attributes.itemId), token: item.attributes.token, amount: item.attributes.amount, price: item.attributes.price, seller: item.attributes.seller, }; // 遍历完成后查看createdArr,内部offer的seller字段出现异常 createdArr.push(offer); }) }
问题原因
- 浏览器控制台对原始类型(字符串、数字)的打印是瞬时值快照,对引用类型(对象、数组)的打印是懒求值——你在遍历阶段打印
item.attributes.seller(字符串,原始类型)正常,只能证明打印瞬间该字段是字符串,无法证明后续逻辑没有修改这个值;等你后续查看createdArr数组时,看到的是对象被修改后的最新值。 - Moralis返回的查询结果是封装后的代理对象,不是普通plain object,直接赋值
item.attributes.seller没有完全切断和原代理对象的引用关联,后续对原对象的修改会同步影响到你赋值的字段。 - 90%的异常来源是后续渲染逻辑的误操作:你在渲染列表/表格时,直接遍历
createdArr给项的seller属性赋值了React按钮元素(比如写了类似item.seller = <button>查看卖家</button>的代码),直接覆盖了原始字符串值,没有单独维护渲染相关的属性。 - 遍历写法不规范:同时使用
map和手动push,map本身会返回新数组,这种混用写法容易埋下引用混乱的隐患。
修复方案
- 构造offer对象时,对所有原始值字段做显式类型转换,彻底切断和Moralis代理对象的引用关联,不要直接赋值代理对象的属性引用。
- 严格分离原始数据和渲染数据:原始数据数组只存接口返回的业务值,渲染需要的React节点单独存到新的渲染数组/渲染配置字段中,绝对不要直接修改原始数据数组的属性。
- 修正遍历写法,统一用
map直接返回构造好的新数组,去掉冗余的手动push逻辑。
修复后的代码参考:
const getEvents = async () => { const created = new Moralis.Query("SaleCreateddd"); const createdResults = await created.find(); // 直接通过map返回构造好的新数组,无需额外声明数组手动push const createdArr = createdResults.map((item) => { // 所有字段显式转成对应的原始类型,彻底切断代理引用 return { itemId: Number(item.attributes.itemId), token: String(item.attributes.token), amount: Number(item.attributes.amount), price: Number(item.attributes.price), seller: String(item.attributes.seller), // 强制转成原始字符串,避免后续引用联动 }; }); // 若需要渲染带按钮的列表,单独生成渲染用数组,禁止修改createdArr原始数据 const renderData = createdArr.map(offer => ({ ...offer, // 渲染用的按钮节点单独存字段,不要覆盖原始seller属性 sellerAction: <button>{offer.seller}</button> })) return createdArr; }
内容的提问来源于stack exchange,提问作者Mandingo San
相关产品推荐
相关产品推荐

