从Redux迁移至Mobx后,如何实例化带注入属性的React组件?
我来帮你梳理下迁移时遇到的这两个问题,都是从Redux转Mobx时很容易踩的坑,咱们一步步解决:
一、TypeScript要求传入@inject的属性
这确实是TypeScript类型定义的问题:@inject是在运行时帮你把store注入到组件props里,但TypeScript编译时不知道这一点,它只会根据你定义的AddressProps接口检查——接口里把report标为必填,所以就会报错要求你手动传入。
解决思路是拆分属性类型,区分“手动传入的属性”和“Mobx注入的属性”,然后让注入的属性变成可选(因为由@inject负责提供):
import { inject, observer } from 'mobx-react'; // 1. 定义Mobx要注入的属性 interface AddressInjectedProps { report: IReportStore; } // 2. 定义需要手动传入的属性(如果没有的话可以留空) interface AddressOwnProps { // 比如:userId: string; } // 3. 合并类型,让注入的属性变成可选(Partial) type AddressProps = AddressOwnProps & Partial<AddressInjectedProps>; @inject((rootStore: RootStore) => ({ report: rootStore.report })) @observer class Address extends React.Component<AddressProps> { // 组件内部可以安全访问this.props.report,因为@inject已经注入了 render() { return <div>{this.props.report?.someData}</div>; } }
这样TypeScript就不会再要求你手动传入report了——它知道这部分属性会由@inject在运行时补上。
如果你想更严谨,也可以用mobx-react提供的InjectedProps工具类型来自动推导注入的属性类型,不过拆分属性的方式更直观,也更容易维护。
二、React-Router属性与Mobx注入的冲突
Redux的container/presenter模式在Mobx里其实不是必须的——Mobx的组件可以直接通过@inject获取store,不需要额外的容器层来做中转。针对你遇到的路由属性问题,有两种优雅的解决方式:
方式1:直接在组件中同时接受路由属性和注入的store
把RouteComponentProps和你的组件属性、注入属性合并,然后在路由配置里直接使用这个组件即可——react-router会自动注入路由属性,Mobx会注入store:
import { inject, observer } from 'mobx-react'; import { RouteComponentProps } from 'react-router-dom'; // 手动传入的属性 interface MyComponentOwnProps {} // 注入的store属性 interface MyComponentInjectedProps { mystore: MyStore; } // 合并所有属性:手动传入 + 路由属性 + 注入属性 type MyComponentProps = MyComponentOwnProps & RouteComponentProps & MyComponentInjectedProps; @inject((rootStore) => ({ mystore: rootStore.mystore })) @observer class MyComponent extends React.Component<MyComponentProps> { constructor(props: MyComponentProps) { super(props); // 可以同时访问路由属性和store console.log(props.match.params.id); console.log(props.mystore.someState); } render() { return <div>...</div>; } } // 路由配置里直接用这个组件 <Route path="/leads/:id" component={MyComponent} />
方式2:保留容器组件(如果习惯这种模式)
如果你还是想保留容器层,只需要把容器组件接收到的路由属性全部传递给子组件即可:
// 容器组件 class MyComponentContainer extends React.Component<RouteComponentProps> { render() { // 把路由属性和store一起传给子组件 return <MyComponent {...this.props} store={mystore} />; } } // 路由配置使用容器组件 <Route path="/leads" component={MyComponentContainer} />
不过更推荐第一种方式——Mobx的设计就是让组件直接和store交互,去掉冗余的容器层会让代码更简洁。
最后再提个小建议
Mobx的核心是让组件成为observer,直接响应store的变化,不需要像Redux那样严格区分container和presenter。你可以慢慢适应这种更灵活的模式,很多时候直接在UI组件里用@inject和@observer就能搞定,代码会比Redux时代更简洁~
内容的提问来源于stack exchange,提问作者jonneve

