dob-refetch 是基于 dob 封装的 dob 的一种实践方案。dob-refetch 类型完美,请求方式简单轻便。


importconnect,{BaseModel,BaseStore,Provider,observable,useDebug}from"dob-refetch";在 Store 中,可以定义待监听的数据、以及改变数据的 action。每个 Store 都和一个 View 一一对应。
其中,AppProps 类型是该 Store 对应的 View 的 Props 类型
@observableclassAppStoreextendsBaseStore<AppProps>{num=1;addNum(num:number){this.num=this.num+num;}}- init 用对应 View 的 Props 初始化
@observableclassAppStoreextendsBaseStore<AppProps>{num=number;init(props:AppProps){this.num=props.num;}}- getProps 获取对应 View 的 Props
@observableclassAppStoreextendsBaseStore<AppProps>{num=number;addNum(){this.num=this.getProps().num+this.num;}}- BaseModel 使用 BaseModel 来做 refetch
BaseModal<返回值类型>(返回值初始值, 对应的请求方法);
如下代码所示,data 是 BaseModel 的实例,fetchData 是对应的请求方法。
其中,data 会自动用请求方法发送请求,并自动处理 loading、success、error,自动触发 rerender。并且,由于 data 对应的请求方法 fetchData 依赖了 this.num,因此当 this.num 改变之后,fetchData 会自动再次执行,触发 data 的更新及 rerender。
@observableclassAppStoreextendsBaseStore<AppProps>{num=1;addNum(num:number){this.num=this.num+num;}data=newBaseModel(""); @bindField("data")privatefetchData(){// dependencies;constnum=this.num;returnmockFetch("I am response data");}}其中 fetchData 也支持 async await 的方式
当需要全局通信是,可以在本地 Store 中,注入其它 Store 的实例,以进行通信。
@observableclassAppStoreextendsBaseStore<AppProps>{num=1;addNum(num:number){this.num=this.otherStore.num+num;}otherStore=this.inject(state=>state.otherStore);}View 需要用 connect 来绑定,connect 第一个参数是从 GlobalState 拿到对应 Store 的 selector。
GlobalState 稍后介绍。
@connect<GlobalState>(state=>state.app)classAppextendsReact.Component<AppProps,any>{render(){const{ store, state}=this.props;constdata=store.data;// 使用全局 stateconstotherNum=state.other.num;return(<div>num:{store.num}<buttononClick={store.addNum.bind(null,3)}>addNum</button>{data.loading ?"loading..." :data.data}</div>);}}// 与redux 的 combineRedux相似,可以随意组装 globalStateconstglobalState={app:AppStore,other:OtherStore};// 拿到 globalState 的类型typeGlobalState=typeofglobalState;ReactDOM.render(<Providerstore={globalState}>{children}</Provider>,document.getElementById("app"));如图:

importconnect,{observable,// Store 基类BaseStore,// 自动请求功能BaseModel,// 类似于 combineReducer,但只做类型转换,不做实事。fixStoreType}from"dob-refetch";@observableclassXStoreextendsBaseStore<XProps>{// 属性区 a='a';b='b';// 复杂属性区complicatedProp={a:'a'};/* * 依赖注入 * 因为单实例的应用都会传到 Provider 里。所以所有的单实例都可以用如下方法注入其它单实例。 */ @inject(AStore)a:AStore;// get 方法区getcomputedName(){returna+b;}// constructor,在实例创建时,如果有逻辑写在这里。constructor(){}/**只在单实例中使用。*单实例中,父级组件willMount时,传入父级的props进行该store的实例初始化。*单实例的初始化使用init。动态实例使用constructor*/init(props:XProps){}// set 方法区changeA(){this.a=a;}// set 方法区可以使用 async awaitasyncchangeA(){awaitpromise1;returnvalue;}asyncchangeB(){// async 方法之间调用和传值constvalue=awaitthis.changeA();}}以上属性、方法的排序,可以在 tslint members-order 进行配置。
注意:
store 中的属性,只能通过调用 store 方法来修改。如果直接用 store.a = 'a2'; 这种方式来修改,dob 会报错。
一种方法是,把属性置为 private 。其优点是是外部既无法直接修改。但是其缺点也是无法读取该属性,可能需要自己写一些重复的 get 方法,比如有些业务直接读取原生数据的 case 不多,更多的是读取衍生数据,那么用这种方法非常优雅。这里用哪种方法,要视业务情况而定,没有固定规定。
Store 规范不变。
View 规范如下:
@connect<GlobalState> (state =>state.a.b)classViewextendsComponent<Props>{}Store 实例在运行时动态生成。比如一个 TODOList 的 TODOListItemStore。onedata 中每个 Tab 的 Store。由于,我们的全局 Store 树始终是静态的,因此这些动态实例,可以动态挂载在 静态树的叶子节点上。
@observableclassTabStoreextendsBaseStore<TabStore>{ sql='';submitSql(){postSql.request({sql:this.sql}).then(()=>{message.success(...);},e=>{message.error(e.message||I18N.message.error);})}}@observableclassTabsextendsBaseStore<TabsProps>{tabItems=[]asTabStore[];createTab(tab:TabStore){this.tabItems.push(tab);}}// 静态全局 Store 树:constglobalStore={menu:MenuStore,header:HeaderStore,frame:FrameStore,tabs:TabStore});typeGlobalStore=ReturnState<typeofglobalStore>;ReturnState 做了一件神奇的事情,转换之前,比如 menu 的类型是一个 Class 。转换之后,它是一个实例。可以通过源码了解一下原理。
Store 规范不变。
View 规范如下:
区别是,connect 不需要任何参数。因为多实例的 Store 不应该绑定任何 store 示例,而是父级在 Props 中传入一个动态的 store,该 store 应该由对应的 Store 创建。
@connectclassViewextendsComponent<Props,xx>{render(){const{ store}=this.props.store;return ...;}}<Viewstore={newStore()}>// 不需要 @observableclassMyStoreextendsBaseStore<Props>{// Store 逻辑不变}// 不需要 @connectclassMyViewextendsReact.Component<Props,any>{// View 逻辑不变}@observableexportclassAStoreextendsMyStore{// 特殊逻辑}constAView=connect<GlobalState>(state=>state.a)(MyView);