You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用MobX将Decimal类实例作为observable属性时调用方法报未定义错误

报错触发的两类核心原因
  • 组件缺少空值兜底逻辑
    Store中value字段的初始值为null,组件未做任何空值判断就直接链式调用mul方法。如果首次渲染时setValue逻辑尚未执行(比如对应交互、异步流程没触发),就会因为值为空导致调用方法失败。
  • MobX的深度可观察逻辑破坏了Decimal实例的原型链
    这是搭配使用MobX和decimal.js时最容易踩的坑:MobX 6+版本默认对observable标记的字段开启深度观察,当把new Decimal(100)的实例赋值给观察字段时,MobX会递归遍历实例的自有属性,将其转换为普通可观察代理对象。这个代理对象只保留遍历到的自有可枚举属性,不会继承Decimal原型上挂载的mul/add/toFixed等方法,最终导致取到的value是没有计算方法的普通对象,调用mul时就会抛出_value.mul is not a function的特定报错(因为此时value并非null,是被篡改后的普通对象)。
可落地的修复方案
  • 为Decimal类型的观察字段关闭深度代理
    将value的观察类型从默认的深度observable改为observable.ref,告知MobX仅对字段的重新赋值动作做响应,不要对字段内部的对象结构做递归代理转换,完整保留Decimal实例的原型链和所有内置方法。修改后的Store构造函数代码:
    constructor() {
      makeObservable(this, {
        // 替换原有的 observable 配置
        value: observable.ref,
        setValue: action,
      });
    }
    
  • 补全组件层面的空值判断
    避免在值为null的初始渲染阶段直接调用实例方法,增加前置分支判断,等Decimal实例完成赋值后再做计算渲染。修改后的组件逻辑:
    const MyPage = observer(() => {
      const value = useStoreValue((rootStore) => rootStore.myStore.value);
      // 空值阶段先渲染占位内容
      if (!value) return <span>--</span>;
      // 确保拿到完整Decimal实例后再调用计算方法
      return <span>{value.mul(5).toString()}</span>;
    });
    
  • 特殊场景适配
    如果确实需要观察Decimal实例内部的属性变化(绝大多数高精度数值场景下不需要,因为Decimal实例本身是不可变值,每次计算都会生成新实例),可以在MobX的可观察配置中自定义对象转换规则,跳过对Decimal类实例的代理处理,避免原型链丢失。

内容的提问来源于stack exchange,提问作者Naor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.26 19:15:04