使用 React&Mobx 的几个最佳实践
source link: https://mp.weixin.qq.com/s?__biz=Mzg2NDAzMjE5NQ%3D%3D&%3Bmid=2247487136&%3Bidx=1&%3Bsn=7d7d76263ff91aa0a5e26e2438f8ee7b
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Mobx 是我非常喜欢的 React 状态管理库,它非常灵活,同时它的灵活也会给开发带来非常多的问题,因此我们在开发的时候也要遵循一些写法上的最佳实践,使我们的程序达到最好的效果。
在 store 中维护业务逻辑
尽量不要把业务逻辑写在 React Component
里面。当你把业务逻辑写在组件里面的时候,很难及时定位错误的,因为业务逻辑分散在各种不同的组件里面,让你很难来通过行为来定义到底是哪些代码涉及的这个错误,不同组件复用这些逻辑也很困难。
最好在 stores
中把业务逻辑编写成方法,并在你的 Component
中调用这些方法。
只允许在 store 中修改属性
尽量不要在一个 Component
里直接修改一个 store
的属性。只有 store
本身可以修改他自己的属性。
当你要改变属性的时候,请调用相应的 store
方法。不然的话你的属性修改会散落在各处不受控制,这是很难调试的。
class Store { @observable text; @action.bound handleSearch = value => { this.text = value } } const store = new Store(); @observer class Home extends Component { handleChanged = (event) => { store.handleSearch(event.target.value); } render() { return ( <input value={store.searchText} onChange={this.handleChanged} /> ); } }
所有属性更改都用 action
使用 action
后,可以清楚的看出哪些代码可以更改可观察的变量,并且方便调试工具给出更多的信息
使用 transaction
可以将多个应用状态( Observable
)的更新视为一次操作,并只触发一次监听者( Reactions
)的动作(UI更新、网络请求等),避免多次重复渲染。 action
中封装了 transaction
,多次改变 @observable
变量时,只会重新渲染一次,提高了性能。
class Store { @observable name; @observable age; @action change(name,age){ this.name = name; this.age = age; } }
从 store 中分离出 API 请求
不要在你的 store
里调用 API
接口,这会让它们很难测试,也让代码变的更复杂和耦合。额外建一个类,把 API
接口调用放进去,并在 store
的构造函数里实例化他们来使用。当你编写测试代码时,你可以很容易地模拟这些 api
并把你的模拟 api
实例传给每一个 store
。
class UserService { fetchUser = () => axios.get('/api/user') } class Store { @observable todos = []; constructor(userService) { this.userService = userService; } fetchUser = async () => { const todos = await this.userService.fetchUser(); runInAction(() => { this.todos = todos; }); } } const userService = new UserService(); const store = new Store(userService);
对每一个 component 都声明 @observer
@observer
可以用来将 React
组件转变成响应式组件。它用 mobx.autorun
包装了组件的 render
函数以确保任何组件渲染中使用的数据变化时都可以强制刷新组件。
给每一个 component
都标注 @observer
,这可以使得他们可以随着 store prop
的改变而更新。如果子组件没有标注 @observer
的话,就会导致其父 component
(有 @observer
)刷新。因此我们要尽可能的让每个子 component
都标注 @observer
,这可以减少不必要的重新渲染。
不要缓存 observables 属性
Observer
组件只会追踪在 render
方法中存取的数据。如果你从 observable
属性中提取数据并将其缓存在组件里,这样的数据是不会被追踪的:
class Store { @observable name; @observable age; } class Home extends React.Component { componentWillMount() { // 错误的,info 的更新不会被追踪 this.info = store.name + store.age } render() { return <div>{this.info}</div> } }
使用 @computed
比如刚刚的例子,使用 @computed
属性来处理一些涉及多个属性的逻辑。使用 @computed
可以减少这样的判断类业务逻辑在组件里面出现的频率。
class Store { @observable name; @observable age; @computed info = () => { return this.name + this.age; } } class Home extends React.Component { render() { return <div>{store.info}</div> } }
多编写受控组件
多编写可控组件,这样会大大降低你的测试复杂度,也让你的组件易于管理。
当需要追踪对象属性时、使用 map
MobX
可以做许多事,但是它无法将原始类型值转变成 observable
(尽管可以用对象来包装它们)。所以说值不是 observable
,而对象的属性才是。这意味着 @observer
实际上是对间接引用值作出反应。所以如果像下面这样初始化的话, Timer
组件是不会作出任何反应的:
ReactDom.render(<Timer timerData={timerData.secondsPassed} />, document.body)
在这行代码中,只是 secondsPassed
的当前值传递给了 Timer
,这个值是不可变值 (JS中的所有原始类型值都是不可变的)。这个值永远都不会改变,所以 Timer
也永远不会更新。 secondsPassed
属性将来会改变,所以我们需要在组件内访问它。或者换句话说: 永远只传递拥有 observable
属性的对象。
如果你想追踪对象中每个属性的变更,可以使用 map
:
observable.map(values?)
创建一个动态键的 observable
映射。如果你不但想对一个特定项的更改做出反应,而且对添加或删除该项也做出反应的话,那么 observable
映射会非常有用。
class Store { timerData = @observable.map({secondsPassed:0}); @action.bound change(value){ this.timerData.set('secondsPassed',value); } } class Home extends React.Component { render() { return <div>{store.timerData.get('secondsPassed')}</div> } }
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK