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

React+TypeScript项目从MobX 5迁移至MobX 6:如何避免冗余代码与语法噪声?

MobX 6 Migration in TypeScript + React: Avoiding Verbose makeObservable Boilerplate

I'm migrating a TypeScript + Create React App project from MobX 5 to MobX 6. Following the official migration guide, I was told to remove all decorators and use makeObservable in the constructor, explicitly defining each field's observable type. However, the guide doesn't cover TypeScript specifics, and all examples are plain JavaScript with ReactDOM.

Here's a simplified version of the class I need to migrate:

@observer
export default class LoginPage extends React.Component<ILoginPageProps, {}> {
    @observable private email: string = process.env.REACT_APP_DEFAULT_LOGIN_EMAIL || "";
    @observable private password: string = process.env.REACT_APP_DEFAULT_LOGIN_PASSWORD || "";

    constructor(props: ILoginPageProps) {
        super(props);
        this.state = {};
    }
}

I manually refactored it to follow the official approach, and it works, but there are major issues:

  • Extreme redundancy: For type safety, each observable property name has to be repeated three times (field declaration, makeObservable type parameter, makeObservable annotation object). For classes with dozens/hundreds of properties, this becomes unmanageable.
  • Refactoring breaks: IDEs can't reliably refactor field or action method names since the names are scattered in unrelated places.
  • Context separation: You can't tell if a method is an action or a getter is a computed property just by looking at their definitions—you have to jump to the constructor.

Worse, the official npx mobx-undecorate tool is incompatible with TypeScript: it spams errors, gets stuck in an infinite CPU loop, and outputs invalid TypeScript (like export default const) that causes naming conflicts.

The official approach has actually made my code worse. Is there a way to migrate to MobX 6 in a TypeScript + React project without bloating the code? Have other developers found a viable workaround?


Great question—this is one of the most common pain points for TS/React devs migrating to MobX 6. The good news is there are several ways to avoid the verbose makeObservable boilerplate while staying compliant with MobX 6's best practices. Here are the most practical solutions:

1. Keep Using Decorators (With MobX 6 Compatibility)

MobX 6 doesn't actually force you to abandon decorators—they're still supported, you just need to pair them with a single makeObservable(this) call in the constructor. This lets you keep your original clean syntax while leveraging MobX 6's improvements.

Setup Steps:

  1. Ensure your tsconfig.json has these settings:
    {
      "compilerOptions": {
        "experimentalDecorators": true,
        "useDefineForClassFields": true
      }
    }
    
  2. If you're using Babel (e.g., with CRA's custom config), add the @babel/plugin-proposal-decorators plugin with legacy: false to match TypeScript's behavior.

Refactored Example:

@observer
export default class LoginPage extends React.Component<ILoginPageProps, {}> {
    @observable private email: string = process.env.REACT_APP_DEFAULT_LOGIN_EMAIL || "";
    @observable private password: string = process.env.REACT_APP_DEFAULT_LOGIN_PASSWORD || "";
    private catpchaKey: number = 0;
    @observable private catpchaToken: string | null = null;
    @observable private errormessage: string = "";
    @observable private overloaymessage: string = "";
    @observable private checking: boolean = false;

    constructor(props: ILoginPageProps) {
        super(props);
        this.state = {};
        // Just this single line—no need to list every property!
        makeObservable(this);
    }

    @computed get loginEnabled(): boolean {
        return !!this.email && isValidEmail(this.email) && !!this.password && this.password.length > 4;
    }

    @action.bound
    verifyCaptchaCallback = (token: string) => {
        this.catpchaToken = token;
    }

    // Rest of your code...
}

This retains all the readability of your original code, works with IDE refactoring tools, and eliminates the redundancy. The makeObservable(this) call picks up all the decorator metadata automatically.

2. Use makeAutoObservable (No Decorators, Minimal Boilerplate)

If you want to ditch decorators entirely but avoid the makeObservable verbosity, use MobX 6's makeAutoObservable function. It automatically infers the observable/computed/action types for class properties and methods.

Refactored Example:

const LoginPage = observer(class LoginPageClass extends React.Component<ILoginPageProps, {}> {
    private email: string = process.env.REACT_APP_DEFAULT_LOGIN_EMAIL || "";
    private password: string = process.env.REACT_APP_DEFAULT_LOGIN_PASSWORD || "";
    private catpchaKey: number = 0;
    private catpchaToken: string | null = null;
    private errormessage: string = "";
    private overloaymessage: string = "";
    private checking: boolean = false;

    constructor(props: ILoginPageProps) {
        super(props);
        this.state = {};
        // Auto-infer all types—no need to list properties!
        makeAutoObservable(this, {
            // Optional: Override inference for specific members if needed
            verifyCaptchaCallback: action.bound
        });
    }

    get loginEnabled(): boolean {
        return !!this.email && isValidEmail(this.email) && !!this.password && this.password.length > 4;
    }

    verifyCaptchaCallback = (token: string) => {
        this.catpchaToken = token;
    }

    // Rest of your code...
})
export default LoginPage;

Key Notes:

  • makeAutoObservable treats:
    • Class fields as observable
    • Getters as computed
    • Methods as action
  • Use the second argument only if you need to override the default inference (e.g., marking a bound method as action.bound, or excluding a property with false).
  • This works seamlessly with TypeScript, and IDE refactoring tools will recognize field/method names since they're only defined once.

3. Fix mobx-undecorate for TypeScript (If You Must)

If you insist on using the official decorator-to-makeObservable path, the mobx-undecorate tool can be made to work with TS by:

  1. Running it on your compiled JavaScript files instead of TypeScript source.
  2. Using a tool like ts-migrate alongside it to fix any type errors.
  3. Checking out community forks of mobx-undecorate that have TS fixes (though this is less reliable than the first two options).

That said, options 1 and 2 are far more maintainable for most projects.


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 08:42:35