{"id":13398849,"url":"https://github.com/AriaFallah/mobx-store","last_synced_at":"2025-03-14T03:30:28.839Z","repository":{"id":57299693,"uuid":"56555308","full_name":"AriaFallah/mobx-store","owner":"AriaFallah","description":"A data store with declarative querying, observable state, and easy undo/redo.","archived":true,"fork":false,"pushed_at":"2016-09-13T03:02:23.000Z","size":238,"stargazers_count":281,"open_issues_count":0,"forks_count":9,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-03-05T17:09:17.774Z","etag":null,"topics":["unmaintained"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/AriaFallah.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"2016-04-19T01:39:52.000Z","updated_at":"2024-11-09T01:56:05.000Z","dependencies_parsed_at":"2022-08-26T21:51:13.915Z","dependency_job_id":null,"html_url":"https://github.com/AriaFallah/mobx-store","commit_stats":null,"previous_names":[],"tags_count":43,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AriaFallah%2Fmobx-store","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AriaFallah%2Fmobx-store/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AriaFallah%2Fmobx-store/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AriaFallah%2Fmobx-store/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AriaFallah","download_url":"https://codeload.github.com/AriaFallah/mobx-store/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243520400,"owners_count":20304137,"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":["unmaintained"],"created_at":"2024-07-30T19:00:32.195Z","updated_at":"2025-03-14T03:30:28.550Z","avatar_url":"https://github.com/AriaFallah.png","language":"JavaScript","funding_links":[],"categories":["Mobx"],"sub_categories":[],"readme":"# mobx-store\n\n[![CircleCI](https://img.shields.io/circleci/project/AriaFallah/mobx-store/master.svg?style=flat-square)](https://circleci.com/gh/AriaFallah/mobx-store)\n[![npm](https://img.shields.io/npm/v/mobx-store.svg?style=flat-square)](https://www.npmjs.com/package/mobx-store)\n[![Coveralls](https://img.shields.io/coveralls/AriaFallah/mobx-store.svg?style=flat-square)](https://coveralls.io/github/AriaFallah/mobx-store)\n\nA data store with declarative querying, observable state, and easy undo/redo.\n\n* [Why](#why)\n  * [Query your data declaratively like it is SQL](#query-your-data-declaratively-like-it-is-sql)\n  * [Schedule reactions to state changes](#schedule-reactions-to-state-changes)\n  * [Easy undo and redo](#easy-undo-and-redo)\n  * [Easy interop with React](#easy-interop-with-react)\n* [Example](#example)\n* [Installation](#installation)\n  * [Keeping your bundle small](#keeping-your-bundle-small)\n* [Tutorial](#tutorial)\n  * [Reading from and writing to the store](#reading-from-and-writing-to-the-store)\n  * [Scheduling reactions to state change](#scheduling-reactions-to-state-change)\n  * [Undo and redo](#undo-and-redo)\n  * [Using with react](#using-with-react)\n* [Credit](#credit)\n\n## Why\n\n#### Query your data declaratively like it is SQL\n```js\nimport mobxstore from 'mobx-store'\nimport { filter, map, pick, sortBy, take } from 'lodash/fp'\n\n// Create store\nconst store = mobxstore({ users: [] })\n\n// SELECT name, age FROM users WHERE age \u003e 18 ORDER BY age LIMIT 1\nstore('users', [map(pick(['name', 'age'])), filter((x) =\u003e x.age \u003e 18), sortBy('age'), take(1)])\n```\n\n#### Schedule reactions to state changes\n```js\nimport mobxstore from 'mobx-store'\nimport { filter } from 'lodash/fp'\n\nfunction log(store) {\n  console.log(store('numbers', filter((x) =\u003e x \u003e 10)))\n}\n\n// Create empty store\nconst store = mobxstore({ numbers: [] })\n\n// Schedule log so that it happens every time the store mutates\nstore.schedule([log, store])\n\n// log is invoked on the push because the store mutated\nstore('numbers').push(1)\n/*\n  logs [] because 1 \u003c 10\n*/\n\n// log is invoked on the push because the store mutated\nstore('numbers').push(12)\n/*\n  logs [12]\n*/\n```\n\n#### Easy undo and redo\n\n```js\nstore('test').push(1, 2, 3) // value of test is [1, 2, 3]\nstore.undo('test') // value of test is [] again\nstore.redo('test') // value of test is [1, 2, 3] again\n```\n\n#### Easy interop with React\n\nOne of the best things about the store is that you can use it with `mobx-react` because it's based\nupon MobX. This also means that when you mutate your objects you don't need setState() calls because\nMobX will handle all the updating for you.\n\n```js\nimport React from 'react'\nimport mobxstore from 'mobx-store'\nimport { observer } from 'mobx-react'\n\nconst store = mobxstore({ objects: [] })\n\nconst Objects = observer(function() {\n  function addCard() {\n    store('objects').push({ name: 'test' })\n  }\n  return (\n    \u003cdiv\u003e\n      \u003cbutton onClick={addCard}\u003eAdd New Card\u003c/button\u003e\n      \u003cdiv\u003e\n        {store('objects').map((o, n) =\u003e\n          \u003cdiv key={n}\u003e\n            {o.name}\n          \u003c/div\u003e\n        )}\n      \u003c/div\u003e\n    \u003c/div\u003e\n  )\n})\n\nexport default Objects\n```\n\n## Example\n\nHere's a quick demo I put together to demonstrate the observable state and undo/redo features. It\nuses the code [you can find later in the README](#scheduling-reactions-to-state-change) to make\nchanges to the store automatically persist to localstorage.\n\n![](http://i.imgur.com/cMCSeOh.gif)\n\n## Installation\n\n```\nnpm install --save mobx-store\n```\n\n#### Keeping your bundle small\n\nIf you're concerned about the extra weight that lodash will add to your bundle you can install\n[babel-plugin-lodash](https://github.com/lodash/babel-plugin-lodash)\n\n```\nnpm install --save-dev babel-plugin-lodash\n```\n\nand add it to your `.babelrc`\n\n```js\n{\n  \"presets\": // es2015, stage-whatever\n  \"plugins\": [/* other plugins */, \"lodash\"]\n}\n```\n\nthis way you can do modular imports, and reduce the size of your bundles on the frontend\n\n```js\nimport { map, take, sortBy } from 'lodash/fp'\n```\n\n## Tutorial\n\nThe store is structured as an object that holds either an array or object for each key.\nFor example, something like\n\n```js\n{\n  numbers: [],\n  ui: {}\n}\n```\n\nTo create a store all you need to do is\n\n```js\nimport mobxstore from 'mobx-store'\n\n// Create empty store and initialize later\nconst store = mobxstore()\nstore.set('users', [])\n\n// Create store with initial state\nconst store = mobxstore({\n  users: [{ name: 'joe', id: 1 }]\n})\n```\n\nand to get access to specific key such as users you would just call.\n\n```js\nstore('users')\n```\n\nWith arrays you can manipulate them as if they are native arrays, but if you made an object you\ninteract with it using the `get` and `set` methods\n\n```js\nstore('ui').get('isVisible')\nstore('ui').set('isVisible', true)\n```\n\n#### Reading from and writing to the store\n\nmobx-store has a simple [lodash](https://github.com/lodash/lodash) powered API.\n\n* Reading from the store is as simple as passing lodash methods to the store function. In order to\npass methods to the store without actually executing them you can import from `lodash/fp`.\n\n* Writing to the store is done by calling the regular array methods as well the methods MobX exposes\nsuch as `replace` on the store object.\n\n```js\nimport { filter } from 'lodash/fp'\nimport mobxstore from 'mobx-store'\n\nconst store = mobxstore({ numbers: [] })\n\nstore('numbers') // read current value of store -- []\nstore('numbers').replace([1, 2, 3]) // write [1, 2, 3] to store\nstore('numbers').push(4) // push 4 into the store\nstore('numbers', filter((v) =\u003e v \u003e 1)) // read [2, 3, 4] from store\n```\n\nYou can also chain methods to create more complex queries by passing an array of functions to the\nstore.\n\n```js\nimport { filter, map, sortBy, take, toUpper } from 'lodash/fp'\nimport mobxstore from 'mobx-store'\n\nconst store = mobxstore({ users: [] })\n\n// Sort users by id and return an array of those with ids \u003e 20\nconst result = store('users', [sortBy('id'), filter((x) =\u003e x.id \u003e 20)])\n```\n\nIf you save the result of one of your queries to a variable,\nyou can continue working with the variable by using the `chain` API\n\n```js\n// Take the top 3, and return an array of their names\nstore.chain(result, [take(3), map('name')])\n\n// Filter again to get those with ids less than 100, take the top 2, and return an array of their names capitalized\nstore.chain(result, [filter((x) =\u003e x.id \u003c 100), take(2), map((v) =\u003e toUpper(v.name))])\n```\n\n#### Scheduling reactions to state change\n\nReacting to state changes is done through the `schedule` API. You pass one to many arrays to the function.\nThe first element of the array is your function, and the following elements are the arguments of your array.\n\nFor example mobx-store comes with an adapter for reading and writing to localstorage, which looks\nlike this.\n\n```js\nfunction read(source) {\n  const data = localStorage.getItem(source)\n  if (data) {\n    return JSON.parse(data)\n  }\n  return {}\n}\n\nfunction write(dest, obj) {\n  return localStorage.setItem(dest, JSON.stringify(obj))\n}\n\nexport default { read, write }\n```\n\nUsing this we can schedule writing to the localstorage whenever the store mutates.\n\n```js\nimport mobxstore from 'mobx-store'\nimport localstorage from 'mobx-store/localstorage'\n\n// Create store initialized with value of localstorage at \"info\"\nconst store = mobxstore(localstorage.read('info'))\n\n// schedule a reaction to changes to the state of the store\nstore.schedule([localstorage.write, 'info', store])\n```\n\nand you're done. Every change you make to this instance of mobx-store will persist to localstorage.\n\n#### Undo and redo\n\nTo use undo and redo pass the name of a key in your store as a parameter. Make sure not to undo if\nyou haven't altered the state of your store, or if you have called it too many times already, and\nlikewise make sure not to call redo if you haven't yet called undo.\n\n```js\nimport mobxstore from 'mobx-store'\n\nconst store = mobxstore({ x: [] })\n\nstore.undo('x') // error\n\nstore('x').push(1)\nstore.undo('x') // undo push\nstore.redo('x') // redo push\n\nstore.redo('x') // error\n```\n\nYou can avoid errors by using the functions `canRedo` and `canUndo`\n\n```js\nif (store.canUndo('x')) {\n  store.undo('x')\n}\nif (store.canRedo('x')) {\n  store.redo('x')\n}\n```\n\nYou can limit the history of the undo by passing `limitHistory` to the store config\n\n```js\n// Can only undo up to 10 times\nconst store = mobxstore({}, { limitHistory: 10 })\n```\n\nLimiting history should be usually be unnecessary as mobx-store doesn't store the entire object in\nhistory like Redux does, which potentially can take up a lot of memory. Instead, it only stores\ninformation about what changed, and only creates the new state when you call undo or redo.\n\n#### Using with React\n\nRead and apply the instructions you can find at [mobx-react](https://github.com/mobxjs/mobx-react)\nto make your components update when your store updates. The gist of it is that you just\n\n```js\nimport { observer } from 'mobx-react'\n```\n\nand wrap the component that is using your store in it.\n\n## Credit\n\n* Thanks to @mweststrate for writing https://github.com/mobxjs/mobx\n* The declarative querying is an improvement upon the cool ideas here https://github.com/typicode/lowdb\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAriaFallah%2Fmobx-store","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FAriaFallah%2Fmobx-store","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAriaFallah%2Fmobx-store/lists"}