{"id":13511314,"url":"https://github.com/unadlib/usm","last_synced_at":"2025-04-08T02:43:11.098Z","repository":{"id":34071866,"uuid":"168468458","full_name":"unadlib/usm","owner":"unadlib","description":"🏖 A concise \u0026 flexible state model for Redux/MobX/Vuex, etc.","archived":false,"fork":false,"pushed_at":"2024-09-26T18:13:12.000Z","size":3834,"stargazers_count":297,"open_issues_count":3,"forks_count":26,"subscribers_count":9,"default_branch":"master","last_synced_at":"2024-10-24T22:17:13.601Z","etag":null,"topics":["angular","immer","mobx","redux","usm","vuex"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/unadlib.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-01-31T05:30:14.000Z","updated_at":"2024-09-26T18:11:42.000Z","dependencies_parsed_at":"2023-02-19T10:00:37.321Z","dependency_job_id":"13b078de-8de9-454b-972f-d19cad6cddca","html_url":"https://github.com/unadlib/usm","commit_stats":{"total_commits":251,"total_committers":2,"mean_commits":125.5,"dds":0.05179282868525892,"last_synced_commit":"9367edf0e2ab9cbf18946e671c96dc237445a85e"},"previous_names":[],"tags_count":94,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unadlib%2Fusm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unadlib%2Fusm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unadlib%2Fusm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unadlib%2Fusm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/unadlib","download_url":"https://codeload.github.com/unadlib/usm/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247767232,"owners_count":20992538,"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":["angular","immer","mobx","redux","usm","vuex"],"created_at":"2024-08-01T03:00:47.180Z","updated_at":"2025-04-08T02:43:11.078Z","avatar_url":"https://github.com/unadlib.png","language":"TypeScript","readme":"# USM\n\n![Node CI](https://github.com/unadlib/usm/workflows/Node%20CI/badge.svg)\n[![npm](https://img.shields.io/npm/v/usm.svg)](https://www.npmjs.com/package/usm)\n\nUSM is a universal state modular library, supports Redux(5.x), MobX(6.x), Vuex(4.x) and Angular(2.0+).\n\n## Motivation\n\n`usm` provides a generic state model that is class-first, which help us to be OOP at almost no cost and is compatible with the ecology of every state library.\n\nWhen you don't want to learn the paradigm of any state library, `usm` can help you use any state library. When your project's business code is based on `usm`, the architecture will be more flexible.\n\n## Support\n\n| Libraries/Frameworks                                       |   None    |    Redux    |    MobX    |    Vuex    | Angular2+ |\n| :--------------------------------------------------------- | :-------: | :---------: | :--------: | :--------: | :-------: |\n| Package Name                                               |   `usm`   | `usm-redux` | `usm-mobx` | `usm-vuex` |   `usm`   |\n| Integrated [Mutative](https://github.com/unadlib/mutative) |    ✅     |     ✅      |     🚫     |     🚫     |    ✅     |\n| State Type                                                 | Immutable |  Immutable  | Observable | Observable | Immutable |\n\n## Installation\n\nTo install `usm`:\n\n```bash\nyarn add usm # npm install --save usm\n```\n\n**And if you want to use Redux/MobX/Vuex, you just install `usm-redux`/`usm-mobx`/`usm-vuex`.**\n\n## Usage\n\n- Use `@state` to decorate a module state.\n\n- Use `@action` to decorate a module method for state changes.\n\n- Use `createStore` to create a store.\n\n```ts\nimport { state, action, createStore } from 'usm';\n// You can also use `usm-redux`, `usm-mobx`, or`usm-vuex`.\n\nclass Counter {\n  @state\n  count = { sum: 0 };\n\n  @action\n  increase() {\n    this.count.sum += 1;\n  }\n}\n\nconst counter = new Counter();\n\nconst store = createStore({\n  modules: [counter],\n});\n\ncounter.increase();\n\nconst newState = Object.values(store.getState())[0] as Counter;\nexpect(newState.count).toEqual({ sum: 1 });\n```\n\n## Examples\n\n- [React](https://github.com/unadlib/usm-redux-demo)\n- [Vue](https://github.com/unadlib/usm-vuex-demo)\n\n## APIs\n\n### `@state`\n\nDefine a shared state for a module, and you can use `@state` for decoration. When use `usm-redux`, the state is not allowed to be `undefined`.\n\nFor example,\n\n```ts\nclass Counter {\n  @state\n  number = 0;\n}\n```\n\n### `@action`\n\nAll operations that change state must be in a method decorated by `@action`.\n\nFor example,\n\n```ts\nclass Counter {\n  @state\n  number = 0;\n\n  @action\n  increase() {\n    this.number += 1;\n  }\n}\n```\n\n### `@computed/@computed()`\n\nIt is used for computing derived data.\n\n\u003e When use `usm-mobx` or `usm-vuex`, you just use `@computed`, Since it is an observable model, its dependency collection is automatic.\n\n\u003e When using `usm` or `usm-redux`, you should also use `@computed`. Since it is a signal model, dependency collection is automatic. However, if you are using storage middleware (e.g., [reactant-storage](https://github.com/unadlib/reactant/blob/master/packages/reactant-storage/src/storage.tsx#L138)), you need to manually update the signal state to trigger reactivity.\n\nFor example,\n\n```ts\nclass Counter {\n  @state\n  count = { sum: 0 };\n\n  @state\n  number = 0;\n\n  @action\n  increase() {\n    this.number += 1;\n  }\n\n  @computed\n  get sum() {\n    return this.count.sum + this.number;\n  }\n}\n```\n\n- If you want to manually control dependencies with `usm` or `usm-redux`, you can use `@computed(depsCallback)`, The return value of the `depsCallback` is an array of dependent value collections that tells the module that its getter will recompute when there is a change in any of the values in the value collections:\n\nFor example,\n\n```ts\nclass Counter {\n  @state\n  count = { sum: 0 };\n\n  @state\n  number = 0;\n\n  @action\n  increase() {\n    this.number += 1;\n  }\n\n  @computed((that) =\u003e [that.count.sum, that.number])\n  get sum() {\n    return this.count.sum + this.number;\n  }\n}\n```\n\n### `createStore()`\n\nCreates a `usm` store that holds the complete shared state.\n\n#### Arguments\n\n- `options`(_object_)\n  - `modules`(_array_): an array with all modules instances\n  - [`strict`] (_boolean_): enable strict mode\n- [`preloadedState`] (_any_): preloaded state\n- [`plugins`/`middleware`] (_any_[]): vuex's plugins or redux's middleware\n\nFor example,\n\n```ts\nclass Counter {\n  @state\n  number = 0;\n\n  @action\n  increase() {\n    this.number += 1;\n  }\n}\n\nconst counter = new Counter();\n\nconst store = createStore({\n  modules: [counter],\n});\n```\n\n### `subscribe()`\n\nYou can use `subscribe()` to subscribe state changes in any class module.\n\nFor example,\n\n```ts\nclass Counter {\n  constructor() {\n    subscribe(this, () =\u003e {\n      //\n    });\n  }\n\n  @state\n  count = { sum: 0 };\n}\n```\n\n### `watch()`\n\nYou can use `watch()` to observe a specific state changes in any class module.\n\nFor example,\n\n```ts\nclass Counter {\n  constructor() {\n    watch(\n      this,\n      () =\u003e this.count.sum,\n      (newValue, oldValue) =\u003e {\n        //\n      }\n    );\n  }\n\n  @state\n  count = { sum: 0 };\n}\n```\n\nYou can pass the option `{ multiple: true }`, which will support watching multiple values.\n\nFor example,\n\n```ts\nclass Counter {\n  constructor() {\n    watch(\n      this,\n      () =\u003e [this.count0, this.count1],\n      ([newCount0, newCount1], [oldCount0, oldCount0]) =\u003e {\n        //\n      },\n      {\n        multiple: true,\n      }\n    );\n  }\n\n  @state\n  count0 = 0;\n\n  @state\n  count1 = 0;\n}\n```\n\n\u003e `watch` option supports passing in `isEqual` function for custom equal.\n\n## License\n\nUSM is [MIT licensed](https://github.com/unadlib/usm/blob/master/LICENSE).\n","funding_links":[],"categories":["cross framework","State Management"],"sub_categories":["Other State Libraries"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funadlib%2Fusm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Funadlib%2Fusm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funadlib%2Fusm/lists"}