如何使用 React + TypeScript 从 MobX 5 迁移到 MobX 6,而不添加带有 200% 语法噪音的臃肿代码?

Howto migrate from MobX 5 to MobX 6 with React + TypeScript without adding bloated code with 200% syntax noise?

我正在尝试将我的 TypeScript / create-react-app 应用程序从 MobX 5 迁移到 MobX 6。在官方迁移指南 (https://mobx.js.org/migrating-from-4-or-5.html#upgrading-classes-to-use-makeobservable) 中,他们建议如下:

Remove all decorators and call makeObservable in the constructor and explicitly define which field should be made observable using which decorator.

但他们没有说明如何使用 TypeScript 应用程序执行此操作。给出的示例总是直接使用普通 JavaScript 和 React.RenderDOM

这是一个简化示例的开头 - 我需要迁移的典型 class:

@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 = {};
    }

“官方”方法是将 makeObservable 调用添加到构造函数,并在那里设置注释。我已经设法手动重构了一个实际的 class,并且它有效:

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 = {};
        makeObservable<LoginPageClass,"email"|"password"|"catpchaKey"|"catpchaToken"|"errormessage"|"overloaymessage"|"checking"|"loginEnabled"|"verifyCaptchaCallback">(this, {
            email: observable,
            password: observable,
            catpchaKey: observable,
            catpchaToken: observable,
            errormessage: observable,
            overloaymessage: observable,
            checking: observable,
            loginEnabled: computed,
            verifyCaptchaCallback: action
        })
    }

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

    verifyCaptchaCallback = (token: string) => {
        this.catpchaToken = token;
    }
    
    // More methods and code here...
}
export default LoginPage;

重构后的代码问题多得无法形容。仅举几例:

他们建议的另一件事是使用 npx mobx-undecorate - 但它不适用于 TypeScript。我试过了,它只是弄乱了我的代码。最终陷入了一个无限循环,耗尽了我所有的 CPU,打印了无尽的回溯等。最后它产生了如下代码:

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

    constructor(props: ILoginPageProps) {
        super(props);

        makeObservable<LoginPage, "email" | "password">(this, {
            email: observable,
            password: observable,
        });

        this.state = {};
    }

这太荒谬了,甚至不是有效的 TypeScript 代码。 (“export default const”只是无效的语法。)它用一个函数调用替换了我的 class,同时创建了一个名称碰撞。 class 的函数名称和名称相同。

据我所知,推荐的方法使一切变得更糟。

所以最后我的问题是:有没有一种方法可以将 React/MobX/TypeScript 代码从 MobX 5 重构到 MobX 6,而不会使代码变得一团糟?有人找到实现此目标的方法吗?

好吧,看来您已经尝试了三种迁移方式中的第一种,但并不喜欢它。但这种方式仅在以下情况下推荐:

This is the recommended approach if you want to drop decorators in your code base, and the project isn't yet too big.

但我同意这种写 stores 的方式对于 TS 来说确实很困难,很多重复等等

如果您想继续使用装饰器,只需将它们保留在原处,然后在构造函数中调用 makeObservable(this),就像文档所说的那样:

Leave all the decorators and call makeObservable(this) in the constructor. This will pick up the metadata generated by the decorators. This is the recommended way if you want to limit the impact of a MobX 6 migration.

还有第三种方法,我认为这是目前最好的方法,没有任何臃肿:

Remove decorators and use makeAutoObservable(this) in the class constructor's.

我只是引用了您已经阅读过的文档,我无法建议任何其他迁移方式。如果你有非常大的项目和装饰器,那么首先使用第二种方式,然后如果你喜欢的话逐渐迁移到第三种方式。