React/Mobx无法覆盖Props设置的默认值问题求助
问题描述
- 使用Mobx的ViewModel类存储值,若组件通过props设置该类属性,后续修改属性值会立刻还原;不通过props设置则一切正常
- 点击修改按钮时,
withDefault的setter会触发三次:第一次是设置的新值,后两次自动还原为props传入的默认值
核心原因
- 渲染时的重复赋值:
Child1GenericComponent的函数体中直接写了vm.withDefault = props.defaultValue as string;,这行代码会在每次组件渲染时执行。当你点击按钮修改vm的值,Mobx会通知所有依赖该可观察对象的observer组件重新渲染,此时Child1GenericComponent会再次执行这行赋值代码,把props里的默认值重新覆盖到vm上,导致刚修改的值被还原。 - 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
相关产品推荐
相关产品推荐

