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

React/Mobx无法覆盖Props设置的默认值问题求助

问题描述
  • 使用Mobx的ViewModel类存储值,若组件通过props设置该类属性,后续修改属性值会立刻还原;不通过props设置则一切正常
  • 点击修改按钮时,withDefault的setter会触发三次:第一次是设置的新值,后两次自动还原为props传入的默认值
核心原因
  1. 渲染时的重复赋值:Child1GenericComponent的函数体中直接写了vm.withDefault = props.defaultValue as string;,这行代码会在每次组件渲染时执行。当你点击按钮修改vm的值,Mobx会通知所有依赖该可观察对象的observer组件重新渲染,此时Child1GenericComponent会再次执行这行赋值代码,把props里的默认值重新覆盖到vm上,导致刚修改的值被还原。
  2. Mobx observer的渲染触发:vm是Mobx可观察对象,修改其属性会触发所有依赖它的observer组件(Parent、Child1、Child2)重新渲染。每次Child1GenericComponent渲染都会执行那行赋值代码,所以setter会被多次调用——第一次是手动修改,之后是组件重新渲染时的两次赋值触发。
解决方案

方案1:用useEffect控制初始化时机

把props赋值逻辑放到useEffect中,通过空依赖数组确保仅在组件挂载时执行一次:

import { observer } from "mobx-react"
import { useEffect } from "react";
import { IParentGenericComponentProps } from "./ParentGenericComponent"
import { ParentGenericComponentViewModel } from "./ParentGenericComponentViewModel"

export const Child1GenericComponent = observer((props: IParentGenericComponentProps) => {
    const vm: ParentGenericComponentViewModel = props.vm;
    
    useEffect(() => {
        if (props.defaultValue) {
            vm.withDefault = props.defaultValue;
        }
    }, []); // 空依赖数组,仅组件挂载时执行一次

    const changeValues = (e: React.MouseEvent) => {
        e.preventDefault(); // 阻止a标签默认跳转行为
        vm.withDefault = "withDefault changed by clicking link";
        vm.withoutDefault = "withoutDefault changed by clicking link";
    }

    return (
        <>
            <h3>Child 1 Generic Component</h3>
            <div>withDefault value: {vm.withDefault}</div>
            <div>withoutDefault value: {vm.withoutDefault}</div>
            <a href="#" onClick={changeValues}>Change Values</a>
            <hr />
        </>
    )
})

方案2:ViewModel构造时传入默认值(更合理)

直接在父组件实例化ViewModel时传入默认值,避免子组件处理props赋值逻辑:

修改ParentGenericComponent.tsx

import { observer } from "mobx-react"
import { Child1GenericComponent } from "./Child1GenericComponent"
import { Child2GenericComponent } from "./Child2GenericComponent"
import { ParentGenericComponentViewModel } from "./ParentGenericComponentViewModel"

export interface IParentGenericComponentProps {
    vm?: ParentGenericComponentViewModel,
    defaultValue?: string
}

export const ParentGenericComponent = observer(() => {
    // 实例化时直接传入默认值
    const vm = new ParentGenericComponentViewModel("Default Overridden In Props");

    return (
        <>
           <div>Parent Generic Component</div>
           <div><Child1GenericComponent vm={vm} /></div>
           <div><Child2GenericComponent vm={vm} /></div>
        </>
    )
})

修改ParentGenericComponentViewModel.ts

import { makeAutoObservable } from "mobx";

export class ParentGenericComponentViewModel {
    constructor(defaultValue?: string) {
        makeAutoObservable(this);
        // 构造时初始化默认值
        if (defaultValue) {
            this._withDefault = defaultValue;
        }
    }

    private _withDefault:string = "Initial Default Value";
    private _withoutDefault: string = "Initial WithoutDefault Value";

    get withDefault():string { return this._withDefault; }
    set withDefault(value:string) {
        this._withDefault = value;
    }

    get withoutDefault():string { return this._withoutDefault; }
    set withoutDefault(value:string) {
        this._withoutDefault = value;
    }
}
额外注意
  • React函数组件的函数体每次渲染都会从头执行,因此不能直接在函数体中做状态赋值、API请求这类副作用操作,必须放到useEffect或其他钩子中控制执行时机。
  • Mobx的observer组件会在可观察对象变化时自动重新渲染,这是正常行为,但要避免在渲染过程中修改可观察对象,否则可能导致无限渲染循环。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 17:38:10