{"id":19578372,"url":"https://github.com/infinitered/mst-reference-pool","last_synced_at":"2025-04-27T06:33:18.233Z","repository":{"id":46343900,"uuid":"422703951","full_name":"infinitered/mst-reference-pool","owner":"infinitered","description":"MST Reference Pool is a MobX-State-Tree extension that allows you to use references to a pool of model instances in any store.","archived":false,"fork":false,"pushed_at":"2023-01-13T22:53:21.000Z","size":133,"stargazers_count":23,"open_issues_count":3,"forks_count":0,"subscribers_count":10,"default_branch":"main","last_synced_at":"2024-11-04T07:36:22.203Z","etag":null,"topics":["javascript","mobx","mobx-state-tree","state-management","typescript"],"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/infinitered.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}},"created_at":"2021-10-29T20:23:46.000Z","updated_at":"2024-05-25T22:55:17.000Z","dependencies_parsed_at":"2023-02-09T17:32:01.399Z","dependency_job_id":null,"html_url":"https://github.com/infinitered/mst-reference-pool","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infinitered%2Fmst-reference-pool","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infinitered%2Fmst-reference-pool/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infinitered%2Fmst-reference-pool/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infinitered%2Fmst-reference-pool/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/infinitered","download_url":"https://codeload.github.com/infinitered/mst-reference-pool/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224062742,"owners_count":17249291,"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":["javascript","mobx","mobx-state-tree","state-management","typescript"],"created_at":"2024-11-11T07:10:54.857Z","updated_at":"2024-11-11T07:10:55.689Z","avatar_url":"https://github.com/infinitered.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MST Reference Pool\n\n`mst-reference-pool` is a [MobX-State-Tree](https://mobx-state-tree.js.org) extension that allows you to use references to a pool of model instances in any store.\n\nThink of it like a hidden `types.array` that you can point references to, plus a garbage collector to get rid of any instances that nothing is referencing anymore.\n\n## When would you use mst-reference-pool?\n\nWhenever you have a frequently-changing array of instances and also have other references to those instances, `mst-reference-pool` becomes a good option.\n\nLet's look at an example ... say, an Instagram-like app. There's a feed of posts and also an optional `currentPost`.\n\n```ts\nimport { types } from \"mobx-state-tree\"\nimport { PostModel } from \"./post\"\n\nconst RootStore = types\n  .model(\"RootStore\", {\n    feed: types.array(PostModel),\n    currentPost: types.maybe(types.reference(PostModel)),\n  })\n  .actions((store) =\u003e ({\n    setFeed(newPosts) {\n      store.feed.replace(newPosts)\n    },\n    setCurrentPost(newPost) {\n      store.currentPost = newPost\n    },\n  }))\n```\n\nThe thing with the feed is that you could scroll or refresh, and then the `currentPost` would refer to a post that is no longer in the feed. This causes a reference error.\n\nYou might think you could make the `currentPost` into its own `types.maybe(PostModel)`, but when that post is in view for both the `feed` and the `currentPost`, now you have duplicate data (and identifiers).\n\nThis is where `mst-reference-pool` shines!\n\n## Implementing mst-reference-pool\n\nTaking our Instagram-like app above, let's convert it to use a reference pool.\n\n```diff\nimport { types } from \"mobx-state-tree\"\n+import { withReferencePool } from \"mst-reference-pool\"\nimport { PostModel } from \"./post\"\n\nconst RootStore = types\n  .model(\"RootStore\", {\n+   pool: types.array(PostModel),\n    feed: types.array(types.reference(PostModel)),\n    currentPost: types.maybe(types.reference(PostModel)),\n  })\n+ .extend(withReferencePool(PostModel))\n  .actions((store) =\u003e ({\n    setFeed(newPosts) {\n+     const posts = store.addAllToPool(newPosts)\n      store.feed.replace(posts)\n    },\n    setCurrentPost(newPost) {\n+     const post = store.addToPool(newPost)\n      store.currentPost = post\n    },\n  }))\n```\n\nAs you can see here, the primary difference is that we now have a `pool` prop that contains the posts, and everything else is just a reference to those posts.\n\nBefore we set the `feed`, we add the new posts to the pool with `store.addAllToPool`, and then use those to establish the references.\n\nWe can do the same for a single reference. We just do `store.addToPool` and then set the reference.\n\nThese references will always point to one instance in the pool. If `addToPool` or `addAllToPool` find an existing instance with the same identifier, they'll run `applySnapshot` on that instance instead. This prevents duplicate items in your pool.\n\n## Garbage Collection\n\nThis is great, but without garbage collection, the pool will grow unbounded as the user scrolls through their feed. This is where the pool GC (garbage collector) comes in.\n\nAdd this action to your model, and pass into `poolGC` any property where these posts might have a reference.\n\n```ts\n.actions((store) =\u003e ({\n  gc() {\n    store.poolGC([\n      store.feed,\n      store.currentPost,\n      // perhaps some other store as well?\n      // search results is a common one\n      store.searchStore.filteredPosts,\n      // for nested references, you can spread a map, like so:\n      ...store.categories.map(cat =\u003e cat.posts)\n    ])\n  }\n}))\n```\n\nThe GC is pretty fast, but you might not need to run it after every action. Generally, you'll run the GC anytime you do a larger refresh of your data. You can also run it anytime you remove or replace items from a list or property. Or, if you want, you could just run it every so often on a timer. It's up to you and what your project needs.\n\nI recommend mainly doing it after a refresh.\n\n```ts\n.actions((store) =\u003e ({\n  setFeed(newPosts) {\n    const posts = store.addAllToPool(newPosts)\n    store.feed.replace(posts)\n    store.gc()\n  },\n}))\n```\n\n## Limitations\n\nCurrently, you can only have one reference pool per store. So, if you want more than one reference pool, just make additional stores per model type.\n\nExample:\n\n```ts\nconst FeedStore = types\n  .model(\"FeedStore\", {\n    pool: types.array(PostModel),\n    feed: types.array(types.reference(PostModel)),\n  })\n  .extend(withReferencePool(PostModel))\n\nconst UserStore = types\n  .model(\"UserStore\", {\n    pool: types.array(UserModel),\n    users: types.array(types.reference(UserModel)),\n  })\n  .extend(withReferencePool(UserModel))\n\nconst RootStore = types.model(\"RootStore\", {\n  feedStore: FeedStore,\n  userStore: UserStore,\n})\n```\n\nYou can use a pool across multiple stores; just make sure you pass all relevant references in those other stores into your `gc` action.\n\n## API Reference\n\n### withReferencePool(ModelType)\n\nThis is an MST extension that takes an argument of the entity type that you will be storing in your reference pool.\n\n```ts\nimport { types } from \"mobx-state-tree\"\nimport { withReferencePool } from \"mst-reference-pool'\nimport { PostModel } from \"./post\"\n\nconst RootStore = types.model(\"RootStore\", {\n  pool: types.array(PostModel)\n})\n.extend(withReferencePool(PostModel))\n```\n\n### addToPool(instanceSnapshot)\n\nThis is an action on your extended store that adds an instance to the pool. If an instance already exists with that identifier in the pool, it will run `applySnapshot` to the existing instance instead of making a duplicate. Think of it as an add or update action.\n\nIt will return the MST instance that it creates or updates.\n\n```ts\n// ...\n.actions((store) =\u003e ({\n  addPost(newPost) {\n    const post = store.addToPool(newPost)\n    // add reference to it somewhere?\n    store.posts.push(post)\n    store.currentPost = post\n  }\n}))\n```\n\n### addAllToPool(instanceSnapshots)\n\nThis is an action on your extended store that adds multiple instances to the pool. If an instance already exists with that identifier in the pool, it will run `applySnapshot` to the existing instance instead of making a duplicate. Think of it as an add or update action for multiple items.\n\nIt will return an array of the MST instances that it creates or updates.\n\n### poolGC([ ...listOfReferences ])\n\nThis is an action on your extended store that garbage collects instances in the pool that do not have any living references left.\n\nYou need to provide any references, since those could live anywhere on the tree. These can be single references or arrays of references.\n\nI recommend creating a `gc` action on your store that calls this action and passes in all references.\n\n```ts\n.actions((store) =\u003e ({\n  gc() {\n    store.poolGC([\n      store.feed,\n      store.currentPost,\n      // perhaps some other store as well?\n      // search results is a common one\n      store.searchStore.filteredPosts\n    ])\n  }\n}))\n```\n\n# Troubleshooting / Tips\n\n1. Make sure you have a pool in the store that is an array of the model type you want to store\n2. Make sure other properties are references or safeReferences\n3. Make sure to run the GC regularly (see the Garbage Collection section above)\n4. Feel free to join the [Infinite Red Community](https://community.infinite.red) to ask questions in our #mobx-state-tree channel\n\n# License\n\nThis project is copyright 2021 by Infinite Red, Inc., and licensed under the MIT license.\n\n# Further Information\n\n- Learn about [MobX-State-Tree](https://mobx-state-tree.js.org)\n- Check out the original live streams where Jamon Holmgren built the first version of this: [Links coming]() \u0026 [Soon]()\n- Learn more about [Infinite Red](https://infinite.red)\n- Join Jamon on Mondays, Wednesdays, and Fridays on his [Twitch stream](https://twitch.tv/jamonholmgren) to hang out while he works on React Native, MobX-State-Tree, and more!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finfinitered%2Fmst-reference-pool","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finfinitered%2Fmst-reference-pool","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finfinitered%2Fmst-reference-pool/lists"}