{"id":31551343,"url":"https://github.com/betomorrow/micro-stores","last_synced_at":"2025-10-04T18:41:57.965Z","repository":{"id":57100327,"uuid":"390696426","full_name":"BeTomorrow/micro-stores","owner":"BeTomorrow","description":"A light state management library featuring observables and immutability","archived":false,"fork":false,"pushed_at":"2023-10-27T09:09:11.000Z","size":2660,"stargazers_count":6,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-16T03:02:03.701Z","etag":null,"topics":[],"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/BeTomorrow.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}},"created_at":"2021-07-29T11:08:29.000Z","updated_at":"2023-11-15T13:10:54.000Z","dependencies_parsed_at":"2023-10-10T16:13:46.167Z","dependency_job_id":null,"html_url":"https://github.com/BeTomorrow/micro-stores","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/BeTomorrow/micro-stores","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BeTomorrow%2Fmicro-stores","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BeTomorrow%2Fmicro-stores/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BeTomorrow%2Fmicro-stores/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BeTomorrow%2Fmicro-stores/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BeTomorrow","download_url":"https://codeload.github.com/BeTomorrow/micro-stores/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BeTomorrow%2Fmicro-stores/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278358483,"owners_count":25973946,"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","status":"online","status_checked_at":"2025-10-04T02:00:05.491Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":"2025-10-04T18:41:52.993Z","updated_at":"2025-10-04T18:41:57.835Z","avatar_url":"https://github.com/BeTomorrow.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Micro-stores\n\n_A light state management library featuring observables and immutability_\n\n## Usage\n\n```ts\nimport { Store } from \"micro-stores\";\n\nconst bookStore = new Store((id) =\u003e fetch(`https://myApi.com/books/${id}`));\nconst myFavoriteBook = bookStore.getObservable(\"dracula\");\n\nconsole.log(myFavoriteBook.get()); // null\nbookStore.fetch(\"dracula\");\nconsole.log(myFavoriteBook.get()); // { id: \"Dracula\", ... }\n```\n\n# ⚠️ Usage with React Native ⚠️\n\nThis library depends on [**uuid**](https://github.com/uuidjs/uuid). To use _uuid_, and therefore _micro-stores_ with React Native, you need to follow the steps described at https://github.com/uuidjs/uuid#react-native--expo:\n\n- Install react-native-get-random-values\n- Run a `pod install`\n- Import it at the root of your app:  \n  `import 'react-native-get-random-values';`\n\n## Micro-observables\n\nThis library is quite heavily based on [**micro-observables**](https://github.com/BeTomorrow/micro-observables). You may want to take a look at the `Observable` signature there.\n\n## Api\n\nMicro-stores exposes 3 main Stores that can be used to easily manage your application state.\n\n### Store\n\nA simple _Store_ retrieving items using a primary key.\n\n```ts\nimport { Store } from \"micro-stores\";\n\nconst bookStore = new Store((id) =\u003e fetchBook(id));\nbookStore.fetch();\n```\n\nTo create a _Store_, you have to provide a fetcher function retrieving objects using an unique identifier. By default, your store will use the `id` property of the object (if it exists).  \nIf your object doesn't have an `id` property, you will need to use one, or specify another unique property to be used.\n\n**Constructor parameters**\n\n| Parameter  | Type                               | Default Value | Description                                |\n| ---------- | ---------------------------------- | ------------- | ------------------------------------------ |\n| fetch      | (key: string) =\u003e T \\| Promise\\\u003cT\\\u003e | /             | The function retrieving item by its key    |\n| primaryKey | string                             | \"id\"          | The primary key to use to map your objects |\n\n**Builder Methods**\n\n**bindProperty**\n\nYou can enrich your _Store_ by binding a property to another _Store_ using the `bindProperty` method.  \nThis is useful if you want to ensure yours objects are up to date when making changes to its referenced property. The _Store_ will use the referenced property unless it is removed from its own _Store_\n\n**Usage**\n\n```ts\nimport { Store } from \"micro-stores\";\n\nconst userStore = new Store(fetchUser);\nconst bookStore = new Store(fetchBook).bindProperty(\"infos.author\", userStore);\n\nbookStore.fetch(\"dracula\");\nconsole.log(userStore.getObservable(\"bram-staker\").get());\n// { id: bram-stoker, name: \"Bram Stoker\" }\nuserStore.save({ id: \"bram-staker\", name: \"Bram\" });\nconsole.log(bookStore.getObservable(\"dracula\").get().infos.author);\n// { id: \"bram-staker\", name: \"Bram\" }\nuserStore.remove(\"bram-staker\");\nconsole.log(bookStore.getObservable(\"dracula\").get().infos.author);\n// { id: \"bram-staker\", name: \"Bram Staker\" }\n```\n\n**presentProperty**\n\nIt has the same purpose as _bindProperty_, but can be used with lighter objects. This means that when fetching items from your store, it will not populate the presented Store with light values. However, it will update the presented store with updated value if they already exist.\n\n| Method          | Type                                                                | Description                                  |\n| --------------- | ------------------------------------------------------------------- | -------------------------------------------- |\n| bindProperty    | (path: string, referenceStore: Store\\\u003cU\\\u003e) =\u003e Store\\\u003cT\\\u003e            | Binds your item property to another Store    |\n| presentProperty | (path: string, referenceStore: Store\\\u003cPartial\\\u003cU\\\u003e\\\u003e) =\u003e Store\\\u003cT\\\u003e | Presents your item property to another Store |\n\n**Methods and properties**\n\nMain methods and properties:\n\n| Property              | Type                                              | Description                                                                        |\n| --------------------- | ------------------------------------------------- | ---------------------------------------------------------------------------------- |\n| primaryKey            | string                                            | The primary key to use to map your objects                                         |\n| items                 | Observable\\\u003cMap\\\u003cstring, T\\\u003e\\\u003e                    | The observable of the items mapped by their key                                    |\n| getObservable         | (key: string) =\u003e Observable\\\u003cT\\\u003e                  | Retrieve an observable using its key                                               |\n| fetch                 | (key: string) =\u003e Promise\u003cT\u003e                       | Call the Store `fetch` function and saves the received item                        |\n| save                  | (item: T) =\u003e void                                 | Save an item to the Store. If an items exists will the same key, it will be erased |\n| merge                 | (items: T[]) =\u003e void                              | Save several items at once                                                         |\n| remove                | (key: string) =\u003e void                             | Remove an item from the Store                                                      |\n| update                | (key: string, updater: (current: T) =\u003e T) =\u003e void | Update an item using an update callback, if it exists                              |\n| updateProperties      | (item: Partial\u003cT\u003e) =\u003e void                        | Update an items with specified properties, if it exists                            |\n| batchUpdateProperties | (items: Partial\u003cT\u003e[]) =\u003e void                     | Update several items with specific properties, if they exists                      |\n| clear                 | () =\u003e void                                        | Clears the store                                                                   |\n| onDelete              | Signal\u003cstring\u003e                                    | Called when an item is removed from the Store                                      |\n\n### PaginatedStore\n\nA _PaginatedStore_ stores items in an Array and handles pagination for you using _Pages_.\n\n```ts\nimport { PaginatedStore } from \"micro-stores\";\n\nconst bookStore = new PaginatedStore((page) =\u003e fetchBooks(page));\nbookStore.list();\nbookStore.listMore();\nbookStore.listMore();\n```\n\nTo create a _PaginatedStore_, you have to provide a fetcher function retrieving a page of objects using an page number.\n\nA Page is an interface defined by this properties:\n\n```ts\ninterface Page {\n\tcontent: T[];\n\tpage: number;\n\ttotalPages: number;\n\ttotalSize: number;\n}\n```\n\n**Constructor parameters**\n\n| Parameter | Type                                           | Description                               |\n| --------- | ---------------------------------------------- | ----------------------------------------- |\n| fetchList | (page: number) =\u003e Promise\u003cPage\u003cT\u003e\u003e \\| Page\u003cT\u003e) | The function retrieving Page by its index |\n\n**Builder Methods**\n\n**bind**\n\nYou can bind your _PaginatedStore_ to another _Store_ using the `bind` method.  \nThis will allow you to show in your list of items the actual items from the binded _Store_, thus ensuring them to be up to date. The binded _Store_ will also be automatically updated with the values retrieved when listing objects from the _PaginatedStore_\nYou can only bind a _PaginatedStore_ to a _Store_ that stores the exact same interface of objects. Meaning that your _PaginatedStore_ will have to use the same unique identifier property as your simple _Store_.  \nYou can only bind your _PaginatedStore_ to a single _Store_.\n\n**Usage**\n\n```ts\nimport { Store, PaginatedStore } from \"micro-stores\";\n\nconst bookStore = new Store(fetchBook);\nconst favoriteBookStore = new PaginatedStore(fetchBook).bind(bookStore);\n\nfavoriteBookStore.list();\nconsole.log(bookStore.getObservable(\"dracula\").get());\n// { id: \"dracula\", name: \"Dracula\" }\nbookStore.save({ id: \"dracula\", name: \"Dracula 2\" });\nconsole.log(favoriteBookStore.paginatedItems.get().content[0]);\n// { id: \"dracula\", name: \"Dracula 2\" }\nbookStore.remove(\"dracula\");\nconsole.log(favoriteBookStore.paginatedItems.get().content[0]);\n// null\n```\n\n**present**\n\n`present` is very similar to the `bind` building method. The difference being it allows you to present from a _Store_ items that are partials objects stored in a _PaginatedStore_.  \nFor performance purpose, prefer using `bind` over `present` if your _Store_ and your _PaginatedStore_ use the exact same objects.\n\nYour can only `bind` or `present` one single _Store_\n\n| Method  | Type                                                 | Description                                 |\n| ------- | ---------------------------------------------------- | ------------------------------------------- |\n| bind    | (referenceStore: Store\\\u003cT\\\u003e) =\u003e Store\\\u003cT\\\u003e           | Binds your Paginated Store to another Store |\n| present | (referenceStore: Store\\\u003cU extends T\\\u003e) =\u003e Store\\\u003cT\\\u003e | Binds your Paginated Store to another Store |\n\n**Methods and properties**\n\nMain methods and properties:\n\n| Property       | Type                            | Description                                                                          |\n| -------------- | ------------------------------- | ------------------------------------------------------------------------------------ |\n| fetching       | Observable\\\u003cboolean\\\u003e           | Is the store fetching initial items ?                                                |\n| fetchingMore   | Observable\\\u003cboolean\\\u003e           | Is the store fetching more items ?                                                   |\n| paginatedItems | Observable\\\u003cPage\\\u003cT\\\u003e \\| null\\\u003e | The observable page of the items                                                     |\n| list           | () =\u003e void                      | Call the Store `fetchList` function for the first page and erases the existing items |\n| listMore       | () =\u003e void                      | Call the Store `fetchList` function and merge the new items                          |\n\n### MappedStore\n\nA _MappedStore_ stores paginated arrays of items in an Map.  \nIt is quite similar to _PaginatedStore_, also allowing you to store your paginated items according to specified keys.\n\n```ts\nimport { MappedStore } from \"micro-stores\";\n\nconst bookStore = new MappedStore((userId, page) =\u003e fetchFavoriteBooksForUser(userId, page));\nbookStore.list(\"user-1\");\nbookStore.listMore(\"user-1\");\nbookStore.list(\"user-2\");\n```\n\nTo create a _MappedStore_, you have to provide a fetcher function retrieving a page of objects using a mapping key and page number.\n\n**Constructor parameters**\n\n| Parameter | Type                                                       | Description                               |\n| --------- | ---------------------------------------------------------- | ----------------------------------------- |\n| fetchList | (id: string, page: number) =\u003e Promise\u003cPage\u003cT\u003e\u003e \\| Page\u003cT\u003e) | The function retrieving Page by its index |\n\n**Builder Methods**\n\n**bind** and **present**\n\nJust like a _PaginatedStore_, a _MappedStore_ allows you to bind/present another _Store_.\n\n**Methods and properties**\n\nMain methods and properties:\n\n| Property           | Type                                            | Description                                                                                       |\n| ------------------ | ----------------------------------------------- | ------------------------------------------------------------------------------------------------- |\n| getFetching        | (key:string) =\u003e Observable\\\u003cboolean\\\u003e           | Is the store fetching initial items for this key?                                                 |\n| getFetchingMore    | (key:string) =\u003e Observable\\\u003cboolean\\\u003e           | Is the store fetching more items for this key?                                                    |\n| getObservableItems | (key:string) =\u003e Observable\\\u003cPage\\\u003cT\\\u003e \\| null\\\u003e | The observable page of the items                                                                  |\n| list               | (key: string) =\u003e void                           | Call the Store `fetchList` function for this key for the first page and erases the existing items |\n| listMore           | (key: string) =\u003e void                           | Call the Store `fetchList` function for this key and merge the new items                          |\n| clear              | () =\u003e void                                      | Clears the store                                                                                  |\n\n## Usage with React\n\nThis library makes State Management easier for any nodeJS or browser application, and has been especially thought to be used with React.  \nThis is why Micro-stores also gives you hooks to help you manage and retrieve the state of your React project:\n\n### useStore(key, store, fetchStrategy?, additionalDeps?)\n\nReturn the value of the matching the given key, the loading state and the current error. Triggers a re-render when the value changes.\n\n```tsx\nimport { Store, useStore } from \"micro-stores\";\n\nconst bookStore = new Store(fetchBook);\n\nconst DraculaBookView = () =\u003e {\n\tconst { result: book, loading, error } = useStore(\"dracula\", bookStore);\n\n\tif (book) {\n\t\treturn (\n\t\t\t\u003cdiv\u003e\n\t\t\t\t{book.title} from {book.author}\n\t\t\t\u003c/div\u003e\n\t\t);\n\t}\n\tif (loading) {\n\t\treturn \u003cdiv\u003eLoading...\u003c/div\u003e;\n\t}\n\treturn null;\n};\n```\n\n### usePaginatedStore(paginatedStore, fetchStrategy?, additionalDeps?)\n\nReturns a `PaginatedDataResult` of the given paginated store. Triggers a rerender when these properties change.\n\n```tsx\nimport { PaginatedStore, usePaginatedStore } from \"micro-stores\";\n\nconst bookStore = new PaginatedStore(fetchBooks);\n\nconst BookView = () =\u003e {\n\tconst { result: books, listMore, lastPage, loading, moreLoading } = usePaginatedStore(bookStore);\n\n\tif (loading) {\n\t\treturn \u003cdiv\u003eLoading...\u003c/div\u003e;\n\t}\n\treturn \u003cdiv\u003e\n\t\t\u003ch2\u003eBooks\u003c/h2\u003e\n\t\t{books.map(book =\u003e \u003cBookView book={book}/\u003e}\n\t\t{moreLoading \u0026\u0026 \u003cdiv\u003eLoading...\u003c/div\u003e}\n\t\t{!lastPage \u0026\u0026 \u003cbutton onClick={() =\u003e listMore()}\u003eLoad More\u003c/button\u003e}\n\t\u003c/div\u003e\n};\n```\n\n### useMappedStore(key, mappedStore, fetchStrategy?, additionalDeps?)\n\nSimilar to `usePaginatedStore`, only difference being you need to pass in the key you want to fetch.\n\n**PaginatedDataResult**\n\nThe PaginatedDataResult is defined like this:\n\n| Property    | Type                | Description                      |\n| ----------- | ------------------- | -------------------------------- |\n| result      | T[]                 | The current array of results     |\n| loading     | boolean             | Is the first page being loaded   |\n| moreLoading | boolean             | Are more items beeing loaded     |\n| error       | Error \\|null        | Fetching error                   |\n| lastPage    | boolean             | Are all the pages fetched        |\n| totalPages? | number \\| undefined | The number of pages              |\n| totalSize?  | number \\| undefined | The total size of the elements   |\n| list        | () =\u003e void          | Function to fetch the first page |\n| listMore    | () =\u003e void          | Function to fetch the next page  |\n| clear       | () =\u003e void          | Clears the store                 |\n\n## Fetch Strategy\n\nThe hooks above allow you to define a Fetch Strategy to decide how often the data should be fetch:\n\n- **FetchStrategy.Always (default)**: The fetch function is called every time the component mounts or a dependency changes\n- **FetchStrategy.Never**: The fetch function is never called from the hook, you want to handle it yourself\n- **FetchStrategy.First**: The fetch function is only called if there is still no result associated with the data (useful if you already fetched the result in a parent component)\n- **FetchStrategy.Once**: The fetch function is called every time the component mounts.\n\n## Typescript\n\nThis library is entirely written in Typescript, meaning you can benefit from its typings without installing other packages.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbetomorrow%2Fmicro-stores","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbetomorrow%2Fmicro-stores","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbetomorrow%2Fmicro-stores/lists"}