{"id":22972852,"url":"https://github.com/dobjs/dob-refetch","last_synced_at":"2025-08-13T13:31:28.160Z","repository":{"id":57213780,"uuid":"108493513","full_name":"dobjs/dob-refetch","owner":"dobjs","description":null,"archived":false,"fork":false,"pushed_at":"2018-12-24T11:45:26.000Z","size":134,"stargazers_count":8,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-12-11T09:17:50.011Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dobjs.png","metadata":{"files":{"readme":"README.md","changelog":"History.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-10-27T03:11:30.000Z","updated_at":"2018-12-24T11:45:28.000Z","dependencies_parsed_at":"2022-08-29T09:21:38.241Z","dependency_job_id":null,"html_url":"https://github.com/dobjs/dob-refetch","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dobjs%2Fdob-refetch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dobjs%2Fdob-refetch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dobjs%2Fdob-refetch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dobjs%2Fdob-refetch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dobjs","download_url":"https://codeload.github.com/dobjs/dob-refetch/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":229764702,"owners_count":18120673,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-14T23:19:58.688Z","updated_at":"2024-12-14T23:19:59.258Z","avatar_url":"https://github.com/dobjs.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# dob-refetch\n\ndob-refetch 是基于 dob 封装的 dob 的一种实践方案。dob-refetch 类型完美，请求方式简单轻便。\n\n[![npm version](https://badge.fury.io/js/dob-refetch.png)](https://badge.fury.io/js/dob-refetch)\n[![npm downloads](https://img.shields.io/npm/dt/dob-refetch.svg?style=flat-square)](https://www.npmjs.com/package/dob-refetch)\n\n## install\n\n```sh\n$ npm i -S dob-refetch\n```\n\n## usage\n\n### 引用方式\n\n```typescript\nimport connect, {\n  BaseModel,\n  BaseStore,\n  Provider,\n  observable,\n  useDebug\n} from \"dob-refetch\";\n```\n\n### Store\n\n在 Store 中，可以定义待监听的数据、以及改变数据的 action。每个 Store 都和一个 View 一一对应。\n\n其中，AppProps 类型是该 Store 对应的 View 的 Props 类型\n\n```typescript\n@observable\nclass AppStore extends BaseStore\u003cAppProps\u003e {\n  num = 1;\n\n  addNum(num: number) {\n    this.num = this.num + num;\n  }\n}\n```\n\n- init 用对应 View 的 Props 初始化\n\n```typescript\n@observable\nclass AppStore extends BaseStore\u003cAppProps\u003e {\n  num = number;\n\n  init(props: AppProps) {\n    this.num = props.num;\n  }\n}\n```\n\n- getProps 获取对应 View 的 Props\n\n```typescript\n@observable\nclass AppStore extends BaseStore\u003cAppProps\u003e {\n  num = number;\n\n  addNum() {\n    this.num = this.getProps().num + this.num;\n  }\n}\n```\n\n- BaseModel 使用 BaseModel 来做 refetch\n\nBaseModal\u003c返回值类型\u003e(返回值初始值, 对应的请求方法);\n\n如下代码所示，data 是 BaseModel 的实例，fetchData 是对应的请求方法。\n\n其中，data 会自动用请求方法发送请求，并自动处理 loading、success、error，自动触发 rerender。并且，由于 data 对应的请求方法 fetchData 依赖了 this.num，因此当 this.num 改变之后，fetchData 会自动再次执行，触发 data 的更新及 rerender。\n\n```typescript\n@observable\nclass AppStore extends BaseStore\u003cAppProps\u003e {\n  num = 1;\n\n  addNum(num: number) {\n    this.num = this.num + num;\n  }\n\n  data = new BaseModel(\"\");\n\n  @bindField(\"data\")\n  private fetchData() {\n    // dependencies;\n    const num = this.num;\n\n    return mockFetch(\"I am response data\");\n  }\n}\n```\n\n其中 fetchData 也支持 async await 的方式\n\n- inject 依赖注入\n\n当需要全局通信是，可以在本地 Store 中，注入其它 Store 的实例，以进行通信。\n\n```typescript\n@observable\nclass AppStore extends BaseStore\u003cAppProps\u003e {\n  num = 1;\n\n  addNum(num: number) {\n    this.num = this.otherStore.num + num;\n  }\n\n  otherStore = this.inject(state =\u003e state.otherStore);\n}\n```\n\n### View\n\nView 需要用 connect 来绑定，connect 第一个参数是从 GlobalState 拿到对应 Store 的 selector。\n\nGlobalState 稍后介绍。\n\n```typescript\n@connect\u003cGlobalState\u003e(state =\u003e state.app)\nclass App extends React.Component\u003cAppProps, any\u003e {\n  render() {\n    const { store, state } = this.props;\n    const data = store.data;\n\n    // 使用全局 state\n    const otherNum = state.other.num;\n\n    return (\n      \u003cdiv\u003e\n        num: {store.num}\n        \u003cbutton onClick={store.addNum.bind(null, 3)}\u003eaddNum\u003c/button\u003e\n        {data.loading ? \"loading...\" : data.data}\n      \u003c/div\u003e\n    );\n  }\n}\n```\n\n### Provider\n\n- GlobalState\n\n```typescript\n// 与redux 的 combineRedux相似，可以随意组装 globalState\nconst globalState = {\n  app: AppStore,\n  other: OtherStore\n};\n\n// 拿到 globalState 的类型\ntype GlobalState = typeof globalState;\n```\n\n- Provider\n\n```typescript\nReactDOM.render(\n  \u003cProvider store={globalState}\u003e{children}\u003c/Provider\u003e,\n  document.getElementById(\"app\")\n);\n```\n\n## devtool\n\n```typescript\nuseDebug();\n```\n\n如图：\n\n![图片](https://img.alicdn.com/tfs/TB1UuB7aWagSKJjy0FgXXcRqFXa-1206-134.png)\n\n## 规范\n\n## import 路径\n\n```typescript\nimport connect, {\n  observable,\n  // Store 基类\n  BaseStore,\n  // 自动请求功能\n  BaseModel,\n  // 类似于 combineReducer，但只做类型转换，不做实事。\n  fixStoreType\n} from \"dob-refetch\";\n```\n\n## Store 规范\n\n```js\n@observable\nclass XStore extends BaseStore\u003cXProps\u003e {\n  // 属性区\n  a = 'a';\n  b = 'b';\n\n  // 复杂属性区\n  complicatedProp = { a: 'a' };\n\n  /*\n   * 依赖注入\n   * 因为单实例的应用都会传到 Provider 里。所以所有的单实例都可以用如下方法注入其它单实例。\n   */\n  @inject(AStore) a: AStore;\n\n  // get 方法区\n  get computedName() {\n      return a + b;\n  }\n\n  // constructor，在实例创建时，如果有逻辑写在这里。\n  constructor() {}\n\n  /*\n   * 只在单实例中使用。\n   * 单实例中，父级组件 willMount 时，传入父级的 props 进行该 store 的实例初始化。\n   * 单实例的初始化使用 init。动态实例使用 constructor\n   * /\n  init(props: XProps) {}\n\n  // set 方法区\n  changeA() {\n    this.a = a;\n  }\n\n  // set 方法区可以使用 async await\n  async changeA() {\n    await promise1;\n\n    return value;\n  }\n\n  async changeB() {\n    // async 方法之间调用和传值\n    const value = await this.changeA();\n  }\n}\n```\n\n以上属性、方法的排序，可以在 tslint members-order 进行配置。\n\n注意：\n\n- store 中的属性，只能通过调用 store 方法来修改。\n  如果直接用 store.a = 'a2'; 这种方式来修改，dob 会报错。\n\n- 一种方法是，把属性置为 private 。其优点是是外部既无法直接修改。但是其缺点也是无法读取该属性，可能需要自己写一些重复的 get 方法，比如有些业务直接读取原生数据的 case 不多，更多的是读取衍生数据，那么用这种方法非常优雅。这里用哪种方法，要视业务情况而定，没有固定规定。\n\n## Store 单实例\n\nStore 规范不变。\n\nView 规范如下：\n\n```js\n@connect \u003c GlobalState \u003e (state =\u003e state.a.b)\nclass View extends Component\u003cProps\u003e {}\n```\n\n## Store 动态实例\n\n### 含义\n\nStore 实例在运行时动态生成。比如一个 TODOList 的 TODOListItemStore。onedata 中每个 Tab 的 Store。由于，我们的全局 Store 树始终是静态的，因此这些动态实例，可以动态挂载在 静态树的叶子节点上。\n\n```js\n@observable\nclass TabStore extends BaseStore\u003cTabStore\u003e {\n  sql = '';\n\n  submitSql() {\n    postSql.request({ sql: this.sql }).then(() =\u003e {\n      message.success(...);\n    }, e =\u003e {\n      message.error(e.message || I18N.message.error);\n    })\n  }\n}\n\n@observable\nclass Tabs extends BaseStore\u003cTabsProps\u003e {\n  tabItems = [] as TabStore[];\n\n  createTab(tab: TabStore) {\n    this.tabItems.push(tab);\n  }\n}\n\n// 静态全局 Store 树：\nconst globalStore = {\n  menu: MenuStore,\n  header: HeaderStore,\n  frame: FrameStore,\n  tabs: TabStore\n});\n\ntype GlobalStore = ReturnState\u003ctypeof globalStore\u003e;\n```\n\nReturnState 做了一件神奇的事情，转换之前，比如 menu 的类型是一个 Class 。转换之后，它是一个实例。可以通过源码了解一下原理。\n\n### 规范\n\nStore 规范不变。\n\nView 规范如下：\n\n区别是，connect 不需要任何参数。因为多实例的 Store 不应该绑定任何 store 示例，而是父级在 Props 中传入一个动态的 store，该 store 应该由对应的 Store 创建。\n\n```js\n@connect\nclass View extends Component\u003cProps, xx\u003e {\n    render() {\n        const { store } = this.props.store;\n\n        return ...;\n    }\n}\n\n\u003cView store={new Store()}\u003e\n```\n\n## 业务组件复用\n\n### 怎样写可复用业务组件\n\n```typescript\n// 不需要 @observable\nclass MyStore extends BaseStore\u003cProps\u003e {\n  // Store 逻辑不变\n}\n\n// 不需要 @connect\nclass MyView extends React.Component\u003cProps, any\u003e {\n  // View 逻辑不变\n}\n```\n\n### 如何复用组件\n\n```typescript\n@observable\nexport class AStore extends MyStore {\n  // 特殊逻辑\n}\n\nconst AView = connect\u003cGlobalState\u003e(state =\u003e state.a)(MyView);\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdobjs%2Fdob-refetch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdobjs%2Fdob-refetch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdobjs%2Fdob-refetch/lists"}