We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
由于redux需要写很多繁琐的action和reducer,大部分项目也没有复杂到需要用到redux的程度,导致不少人对redux深恶痛绝。mobx是另一种状态管理方案,这里分享一下我最近使用mobx的经验。
我最喜欢mobx的地方就是和vue一样的数据监听,框架底层通过Object.defineProperty或Proxy来劫持数据,对组件可以进行更细粒度的渲染。在react中反而把更新组件的操作(setState)交给了使用者,由于setState的"异步"特性导致了没法立刻拿到更新后的state。
想像一下,在redux中,如果一个值A是由另外几个值B、C、D计算出来的,在store中该怎么实现?
如果要实现这么一个功能,最麻烦的做法是在所有B、C、D变化的地方重新计算得出A,最后存入store。
当然我也可以在组件渲染A的地方根据B、C、D计算出A,但是这样会把逻辑和组件耦合到一起,如果我需要在其他地方用到A怎么办?
我甚至还可以在所有connect的地方计算A,最后传入组件。但由于redux监听的是整个store的变化,所以无法准确的监听到B、C、D变化后才重新计算A。
但是mobx中提供了computed来解决这个问题。正如mobx官方介绍的一样,computed是基于现有状态或计算值衍生出的值,如下面todoList的例子,一旦已完成事项数量改变,那么completedCount会自动更新。
class TodoStore { @observable todos = [] @computed get completedCount() { return (this.todos.filter(todo => todo.isCompleted) || []).length } }
reaction则是和autorun功能类似,但是autorun会立即执行一次,而reaction不会,使用reaction可以在监听到指定数据变化的时候执行一些操作,有利于和副作用代码解耦。
// 当todos改变的时候将其存入缓存 reaction( () => toJS(this.todos), (todos) => localStorage.setItem('mobx-react-todomvc-todos', JSON.stringify({ todos })) )
mobx中的store的创建偏向于面向对象的形式,mobx官方给出的例子todomvc中的store更接近于mvc中的model。
但是这样也会带来一个问题,业务逻辑我们应该放到哪里?如果也放到store里面很容易造成不同store之间数据的耦合,因为业务代码必然会耦合不同的数据。
我参考了dobjs后,推荐将store拆分为action和dataModel两种。
action和dataModel一起组合成了页面的总store,dataModel只存放UI数据以及只涉及自身数据变化的action操作(在mobx严格模式中,修改数据一定要用action或flow)。
action store则是负责存放一些需要使用来自不同store数据的action操作。
我个人理解,dataModel更像MVC中的model,action store是controller,react components则是view,三者构成了mvc的结构。
- stores - actions - hotelListAction.js - dataModel - globalStatus.js - hotelList.js - index.js // globalStatus class GlobalStatus { @observable isShowLoading = false; @action showLoading = () => { this.isShowLoading = true } @action hideLoading = () => { this.isShowLoading = false } } // hotelList class HotelList { @observable hotels = [] @action addHotels = (hotels) => { this.hotels = [...toJS(this.hotels), ...hotels]; } } // hotelListAction class HotelListAction { fetchHotelList = flow(function *() { const { globalStatus, hotelList } = this.rootStore globalStatus.showLoading(); try { const res = yield fetch('/hoteList', params); hotelList.addHotels(res.hotels); } catch (err) { } finally { globalStatus.hideLoading(); } }).bind(this) }
observer可以给组件增加订阅功能,一旦收到数据变化的通知就会将组件重新渲染,从而做到更细粒度的更新,这是redux和react很难做到的,因为react中组件重新渲染基本是依赖于setState和接收到新的props,子组件的渲染几乎一定会伴随着父组件的渲染。 也许很多人没有注意到,mobx-react中还提供了一个Observer组件,这个组件接收一个render方法或者render props。
const App = () => <h1>hello, world</h1>; <Observer>{() => <App />}</Observer> <Observer render={() => <App />} />
也许你要问这个和observer有什么区别?还写的更加复杂了,下面这个例子对比起来会比较明显。
import { observer, Observer, observable } from 'mobx-react' const App = observer( (props) => <h1>hello, {props.name}</h1> ) const Header = (props) => <h1>this is header</h1> const Footer = (props) => <h1>this is footer</h1> const Container = observer( (props) => { return ( <> <Header /> <App name={props.person.name} /> <Footer /> </> ) } ) const person = observable({name: "gyyin"}); render(<Container person={person} />, document.getElementById("app")); person.name = "world";
上面这个代码,Container组件监听到person.name改变的时候会重新渲染,这样就导致了原本不需要重新渲染的Header和Footer也跟着渲染了,如果使用Observer就可以做到更细粒度的渲染。
const App = (props) => <h1>hello, {props.name}</h1> const Header = (props) => <h1>this is header</h1> const Footer = (props) => <h1>this is footer</h1> const Container = (props) => { return ( <> <Header /> <Observer render={ () => <App name={props.person.name} /> }> <Footer /> </> ) } const person = observable({name: "gyyin"}); render(<Container person={person} />, document.getElementById("app")); person.name = "world";
如果在Header和Footer里面做console.log,你会发现只有被Observer包裹的App组件进行了重新渲染,由于Container没有订阅数据变化,所以也不会重新渲染。
但如果不是对性能有极致的追求,observer已经足够了,大量的Observer会花费你很多精力来管理渲染问题。
参考链接:
The text was updated successfully, but these errors were encountered:
No branches or pull requests
由于redux需要写很多繁琐的action和reducer,大部分项目也没有复杂到需要用到redux的程度,导致不少人对redux深恶痛绝。mobx是另一种状态管理方案,这里分享一下我最近使用mobx的经验。
更响应式
我最喜欢mobx的地方就是和vue一样的数据监听,框架底层通过Object.defineProperty或Proxy来劫持数据,对组件可以进行更细粒度的渲染。在react中反而把更新组件的操作(setState)交给了使用者,由于setState的"异步"特性导致了没法立刻拿到更新后的state。
computed
想像一下,在redux中,如果一个值A是由另外几个值B、C、D计算出来的,在store中该怎么实现?
如果要实现这么一个功能,最麻烦的做法是在所有B、C、D变化的地方重新计算得出A,最后存入store。
当然我也可以在组件渲染A的地方根据B、C、D计算出A,但是这样会把逻辑和组件耦合到一起,如果我需要在其他地方用到A怎么办?
我甚至还可以在所有connect的地方计算A,最后传入组件。但由于redux监听的是整个store的变化,所以无法准确的监听到B、C、D变化后才重新计算A。
但是mobx中提供了computed来解决这个问题。正如mobx官方介绍的一样,computed是基于现有状态或计算值衍生出的值,如下面todoList的例子,一旦已完成事项数量改变,那么completedCount会自动更新。
reaction
reaction则是和autorun功能类似,但是autorun会立即执行一次,而reaction不会,使用reaction可以在监听到指定数据变化的时候执行一些操作,有利于和副作用代码解耦。
拆分store
mobx中的store的创建偏向于面向对象的形式,mobx官方给出的例子todomvc中的store更接近于mvc中的model。
但是这样也会带来一个问题,业务逻辑我们应该放到哪里?如果也放到store里面很容易造成不同store之间数据的耦合,因为业务代码必然会耦合不同的数据。
我参考了dobjs后,推荐将store拆分为action和dataModel两种。
action和dataModel一起组合成了页面的总store,dataModel只存放UI数据以及只涉及自身数据变化的action操作(在mobx严格模式中,修改数据一定要用action或flow)。
action store则是负责存放一些需要使用来自不同store数据的action操作。
我个人理解,dataModel更像MVC中的model,action store是controller,react components则是view,三者构成了mvc的结构。
细粒度的渲染
observer可以给组件增加订阅功能,一旦收到数据变化的通知就会将组件重新渲染,从而做到更细粒度的更新,这是redux和react很难做到的,因为react中组件重新渲染基本是依赖于setState和接收到新的props,子组件的渲染几乎一定会伴随着父组件的渲染。
也许很多人没有注意到,mobx-react中还提供了一个Observer组件,这个组件接收一个render方法或者render props。
也许你要问这个和observer有什么区别?还写的更加复杂了,下面这个例子对比起来会比较明显。
上面这个代码,Container组件监听到person.name改变的时候会重新渲染,这样就导致了原本不需要重新渲染的Header和Footer也跟着渲染了,如果使用Observer就可以做到更细粒度的渲染。
如果在Header和Footer里面做console.log,你会发现只有被Observer包裹的App组件进行了重新渲染,由于Container没有订阅数据变化,所以也不会重新渲染。
但如果不是对性能有极致的追求,observer已经足够了,大量的Observer会花费你很多精力来管理渲染问题。
参考链接:
The text was updated successfully, but these errors were encountered: