React.js与Mobx中查找集合元素时出现「TypeError: cyclic object value」错误的排查求助
这个错误看起来有点迷惑——明明功能正常,错误还指向CSS文件,但其实罪魁祸首是React DevTools在序列化Mobx Observable对象时遇到了循环引用,和你的CSS代码完全无关(webpack打包后混淆了文件名才显示成_product.css)。
问题原因
你在组件里用useState存储了Mobx返回的Observable对象:
setProduct(store.findById(+id))
Mobx 6的makeAutoObservable会给数组和对象添加内部的Observable元数据(比如$mobx属性),这些元数据里包含循环引用的结构。当React DevTools试图读取组件的state并序列化为JSON时,就会触发「cyclic object value」错误。
解决方案
有几个简单的修复方式,按推荐程度排序:
1. 直接使用Mobx状态,抛弃useState(最优解)
因为你已经用了observer包裹组件,Mobx会自动追踪状态变化并触发组件重渲染,完全不需要把Mobx的状态转成React的local state。修改组件代码:
const ProductDetail = () => { const { id } = useParams() // 直接通过store获取,不用useState const product = store.findById(+id) // 处理product为undefined的情况(比如id不存在) if (!product) return <div>Product not found</div> const { price, image } = product return ( <section className="product-detail"> <div> Product Page: {id} </div> <div>Price: {price}</div> <div>Image: {image}</div> </section> ) }
这样既简化了代码,又彻底避免了循环引用的问题。
2. 将Observable对象转为普通JS对象(如果一定要用useState)
如果因为某些原因必须使用local state,用Mobx提供的toJS方法把Observable对象转成普通对象(比JSON.parse(JSON.stringify())更可靠,能保留正确的类型):
首先导入toJS:
import { toJS } from "mobx"
然后修改useEffect:
useEffect(() => { const foundProduct = store.findById(+id) setProduct(foundProduct ? toJS(foundProduct) : {}) }, [id])
toJS会剥离Mobx的Observable元数据,得到一个干净的普通对象,DevTools序列化时就不会报错了。
3. 检查React DevTools版本
如果以上方法都没用,可能是你的React DevTools版本和Mobx 6不兼容。尝试更新浏览器的React DevTools扩展,或者暂时禁用DevTools的某些状态检查功能(不推荐,但可以临时排查问题)。
验证
修改后刷新页面,功能依然正常,控制台的「cyclic object value」错误应该会消失。
内容的提问来源于stack exchange,提问作者Serhii

