{"id":33184000,"url":"https://github.com/danieldunderfelt/mobx-app","last_synced_at":"2026-02-01T10:02:02.964Z","repository":{"id":57299578,"uuid":"69997799","full_name":"danieldunderfelt/mobx-app","owner":"danieldunderfelt","description":"A functional structure for mobx","archived":false,"fork":false,"pushed_at":"2018-07-14T05:39:15.000Z","size":74,"stargazers_count":87,"open_issues_count":0,"forks_count":5,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-08-25T01:50:01.869Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","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/danieldunderfelt.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":"2016-10-04T19:15:00.000Z","updated_at":"2025-03-23T18:18:17.000Z","dependencies_parsed_at":"2022-08-26T18:22:13.202Z","dependency_job_id":null,"html_url":"https://github.com/danieldunderfelt/mobx-app","commit_stats":null,"previous_names":[],"tags_count":25,"template":false,"template_full_name":null,"purl":"pkg:github/danieldunderfelt/mobx-app","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danieldunderfelt%2Fmobx-app","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danieldunderfelt%2Fmobx-app/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danieldunderfelt%2Fmobx-app/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danieldunderfelt%2Fmobx-app/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/danieldunderfelt","download_url":"https://codeload.github.com/danieldunderfelt/mobx-app/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danieldunderfelt%2Fmobx-app/sbom","scorecard":{"id":319546,"data":{"date":"2025-08-11","repo":{"name":"github.com/danieldunderfelt/mobx-app","commit":"e99aad3d72c5e821c872d538c74c54a9a9d3d77a"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.3,"checks":[{"name":"Code-Review","score":0,"reason":"Found 1/29 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 2 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"63 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-v88g-cgmw-v5xw","Warn: Project is vulnerable to: GHSA-fwr7-v2mv-hh25","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-cwfw-4gq5-mrqx","Warn: Project is vulnerable to: GHSA-g95f-p29q-9xw4","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-rq8g-5pc5-wrhr","Warn: Project is vulnerable to: GHSA-hr2v-3952-633q","Warn: Project is vulnerable to: GHSA-qrmc-fj45-qfc2","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-8r6j-v8pm-fqw3","Warn: Project is vulnerable to: MAL-2023-462","Warn: Project is vulnerable to: GHSA-xf7w-r453-m56c","Warn: Project is vulnerable to: GHSA-q42p-pg8m-cqh6","Warn: Project is vulnerable to: GHSA-w457-6q6x-cgp9","Warn: Project is vulnerable to: GHSA-62gr-4qp9-h98f","Warn: Project is vulnerable to: GHSA-f52g-6jhx-586p","Warn: Project is vulnerable to: GHSA-2cf5-4w76-r9qv","Warn: Project is vulnerable to: GHSA-3cqr-58rm-57f8","Warn: Project is vulnerable to: GHSA-g9r4-xpmj-mj65","Warn: Project is vulnerable to: GHSA-q2c6-c6pm-g3gh","Warn: Project is vulnerable to: GHSA-765h-qjxv-5f44","Warn: Project is vulnerable to: GHSA-f2jv-r9rf-7988","Warn: Project is vulnerable to: GHSA-44pw-h2cw-w3vq","Warn: Project is vulnerable to: GHSA-jp4x-w63m-7wgm","Warn: Project is vulnerable to: GHSA-c429-5p7v-vgjp","Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj","Warn: Project is vulnerable to: GHSA-qqgx-2p2h-9c37","Warn: Project is vulnerable to: GHSA-2pr6-76vf-7546","Warn: Project is vulnerable to: GHSA-8j8c-7jfh-h6hx","Warn: Project is vulnerable to: GHSA-896r-f27r-55mw","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-4xc9-xhrj-v574","Warn: Project is vulnerable to: GHSA-x5rq-j2xg-h7qm","Warn: Project is vulnerable to: GHSA-jf85-cpcp-j695","Warn: Project is vulnerable to: GHSA-p6mc-m468-83gw","Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-xf5p-87ch-gxw2","Warn: Project is vulnerable to: GHSA-5v2h-r2cx-5xgj","Warn: Project is vulnerable to: GHSA-rrrm-qjm4-v8hf","Warn: Project is vulnerable to: GHSA-f9cm-qmx5-m98h","Warn: Project is vulnerable to: GHSA-7wpw-2hjm-89gp","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-vh95-rmgr-6w4m","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-5fw9-fq32-wv5p","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-6g33-f262-xjp4","Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-mf6x-7mm4-x2g7","Warn: Project is vulnerable to: GHSA-j44m-qm6p-hp7m","Warn: Project is vulnerable to: GHSA-3jfq-g458-7qm9","Warn: Project is vulnerable to: GHSA-5955-9wpr-37jh","Warn: Project is vulnerable to: GHSA-f5x3-32g6-xq36","Warn: Project is vulnerable to: GHSA-jgrx-mgxx-jf9v","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-c4w7-xm78-47vh","Warn: Project is vulnerable to: GHSA-p9pc-299p-vxgp"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-18T01:02:53.970Z","repository_id":57299578,"created_at":"2025-08-18T01:02:53.970Z","updated_at":"2025-08-18T01:02:53.970Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28975278,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-01T09:57:52.632Z","status":"ssl_error","status_checked_at":"2026-02-01T09:57:49.143Z","response_time":56,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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-11-16T04:00:36.902Z","updated_at":"2026-02-01T10:02:02.954Z","avatar_url":"https://github.com/danieldunderfelt.png","language":"JavaScript","funding_links":[],"categories":["Awesome MobX"],"sub_categories":["Examples"],"readme":"mobx-app\n========\n\nThis is my preferred state structure for [mobx](http://mobxjs.github.io/mobx/index.html) projects.\nmobx-app provides a very light (VERY LIGHT!) functional structure over mobx without hiding mobx in any way.\n\nmobx-app was inspired by functional programming and does not use classes or inheritance. Composition all the way!\n\nmobx-app itself is not really a library, it is a theory of how to structure applications that use mobx as their\nstate manager. Mobx does not, as opposed to Redux, provide any structure for your state. In using mobx in React apps,\nI found myself re-creating a simple class-based structure (gotta love them decorators!) for each project. Juggling\ninstances got old quick, so I sought inspiration from functional programming on how to untangle\nthe mess. mobx-app is the (still work-in-progress!) result.\n\nInstall\n-------\n\nWith npm:\n\n`npm install --save mobx-app`\n\nmobx-app also relies on some peer dependencies that you probably already have in your project:\n\n`npm install --save lodash mobx`\n\nOverview\n--------\n\nThe core concepts of mobx-app:\n\n- The state is a single observable object (hello Redux!)\n- The various facets of the state is expressed in `stores`\n- Stores are functions that receive the state object and optionally initial data\n- Store functions return actions\n- Store functions extend the state they receive with their own state.\n- Initial data (server-side rendering, localstorage) is a first-class citizen\n- Use `computed` as much as possible\n- Actions (and ONLY actions) mutate state directly\n\n#### Stores\nStores are simple functions with the following signature:\n\n```\n(state, initialData) =\u003e actions\n```\n\nThey exist to bootstrap a slice of your state and add reactions or other subscriptions. Most importantly, they\nreturn actions. I recommend putting actions in a separate file so that they can be reused in other places.\n\nAn example of a store factory:\n\n```javascript\nimport { extendObservable } from 'mobx'\n\nconst Store = (state, initialData) =\u003e {\n\n    extendObservable(state, {\n        some: 'data',\n        items: []\n    })\n    \n    // Initialize actions with the state\n    const actions = storeActions(state)\n    \n    // Run initial setup\n    actions.setItems(initialData.items)\n\n    return actions\n}\n```\n\nHere we are using the standard `extendObservable` function from mobx to extend new state properties onto the\nglobal state. When we compose actions, we can feed the whole state object to the action factory. Actions do not\nneed to be tied to a store, but any store can import any action factory and use it. The actions are then\nreturned from the store in order to be available under the store name on the global actions object.\n\nIf you need namespaces in the state, just add your store properties under a namespace. It's that simple.\nNo special mechanisms.\n\nAny store setup should be performed after the state has been extended and the actions created. The initial\nsetup procedures can then freely use any actions and state that the store makes available. This is a good\nopporunity to add `reaction`s, any socket subscriptions or things like that.\n\nThe store setup is also where you can use the store actions to apply any initial data the store might receive.\nOf course, if the data can be imported into the store without going through actions, feel free to assign it\nwhen you extend the global state. I do however recommend that ANY data that goes into the state goes through your\nactions.\n\n#### Actions\nActions are simple functions that (usually) mutate the state. Mutator actions are wrapped in mobx `action`s.\nAs already discussed, actions are most conveniently created from a factory that receives the state. Use\ncomposition to efficiently create specific actions that build on more general ones! In fact, mobx-app ships\nwith a handful of actions for setting values and working with collections. You know, the things you usually\ndo in an app.\n\n\"Actions\" can also be functions that do not directly mutate the state, for example fetcher functions for your api.\nI include these in the umbrella term `actions` and even define them in the same factory as other actions.\nThey are, after all, actions that your app can perform.\n\nAn example of an action factory:\n\n```javascript\nimport { action } from 'mobx'\n\nconst storeActions = state =\u003e {\n\n    const replaceItems = action((items) =\u003e {\n        state.items.replace(items)\n    })\n    \n    const addItem = action((item) =\u003e {\n      state.items.push(item)\n    })\n    \n    async function fetchItems(params) {\n        const newItemsReq = await fetch('https://example.com/api/items')\n        const newItems = await newItemsReq.json()\n        replaceItems(newItems)\n    }\n    \n    return {\n        fetchItems,\n        replaceItems,\n        addItem\n    }\n}\n```\n\nIn its simplest form, the actions tap into the state and perform mutations on it. You may want to pass in the\nname of the state property that these actions should concern, but I like to keep it simple.\n\n#### createStore\nYou might have noticed that we haven't used a single thing you just installed with npm yet. That's the point;\nmobx-app is more of a concept than a library. The library `mobx-app` is simply a few helpers for implementing\nthis state structure.\n\nAt the heart of mobx-app is the `createStore` function that creates your single state and actions. It should\nreceive a map of store functions, loop through it, calling each function with the state and initial data as\nit goes. Anything returned from the store function gets assigned to an `actions` object under the key from\nthe initial store map. The result is a map of actions, bearing the same keys as the initial map of stores,\nas well as the single state object.\n\nAn example:\n\n```javascript\nimport { createStore } from 'mobx-app'\n\nconst stores = {\n  Store\n}\n\nconst { state, actions } = createStore(stores, initialData)\n```\n\nSince mobx-app is unabashedly React-focused, the next step is to use `Provider` from `mobx-react` to add your\n`state` and `actions` to the context of your components:\n\n```javascript\nimport { Provider } from 'mobx-react'\n\nconst app = (\n  \u003cProvider actions={ actions } state={ state }\u003e\n    \u003cYourApp /\u003e\n  \u003c/Provider\u003e\n)\n```\n\nAccess these with `@inject` from `mobx-react` using the `app` helper as a selector function. `app` returns a\nfunction that instructs `@inject` which props to pass the component. `app` can take a list of store\nnames as arguments, that will result in the corresponding actions to be injected as props under the name.\n\nRemember to still use `observer`!\n\n```javascript\nimport { observer } from 'mobx-react'\nimport { app } from 'mobx-app'\n\n@inject(app('Store'))\n@observer\nclass MyComponent extends Component {\n  \n  render() {\n    const { Store, state } = this.props\n    \n    return (\n      \u003cdiv\u003e\n        { state.some } // prints 'data'\n        \u003cbutton onClick={ () =\u003e Store.addItem({ name: 'new item' }) } /\u003e\n      \u003c/div\u003e\n    )\n  }\n}\n```\n\n#### Included actions\nmobx-app ships with actions for working with collections (arrays of objects) and values.\n\nThe value actions exist so that you do not have to wrap all your actions in `action`, you can just create\nnew actions for a specific value and call those actions from your higher-order action.\n\nThe collection actions wrap an array of objects and enable you to easily add, get, update and remove objects.\nThe add actions can either simply replace all items, or add new items while checking for uniqueness. You can\nalso specify a factory function that all new items go through automatically. Please see the api section below\nfor more specific information, but here's an example:\n\n```javascript\nimport { collection, value } from 'mobx-app'\n\nconst itemFactory = itemData =\u003e { ... } // Do something with each item before adding it.\n\nconst yourActions = state =\u003e {\n  const itemActions = collection(state.items, itemFactory)\n  const someActions = value(state, 'some') // Remember the `some` property from the store above ;)\n  \n  function addItems(stuff) {\n    return itemActions.addItems(stuff)\n    // state.items will now contain everything from `stuff` that didn't exist before\n  }\n  \n  function setSome(newValue) {\n    return someActions.setItem(newValue)\n    // Also includes `extendItem` for object values what you want to extend.\n  }\n  \n  return {\n    ...itemActions,\n    setSome,\n    addItems // The new addItems action will override the addItem action from `ìtemActions` for each consumer of these actions. \n  }\n}\n```\n\nAPI\n---\n\n### `createStore()`\n\nReceives 1. a map of stores:\n\n```javascript\nconst stores = {\n  ItemStore: itemStore,\n  OtherStore: otherStore\n}\n```\n\nand 2. an object representing initial data. createStore returns an object with the keys `state`, which is a mobx-observable\nobject, and `actions` which is a plain object with the same keys as above, but containing the actions returned\nfrom the respective stores. The state is not partitioned according to the keys by default.\n\nThe store factories passed through `createStore` will be called with the following arguments:\n\n`store(state, initialData, name)`\n\nState is the global state, initialData is the initial data and name is the key that the store was registered under\nin the initial store map that you passed to `createStore`. Use the name for namespacing the state if you need to.\n\n---\n\n### `@app()`\n\nSelector function factory for mobx-react's `inject`.\n\nReceives an argument list of arbitrary length of action names to add to the `props` of React components.\nIt will also in all cases add the state as a prop of the component. If called with no arguments, all\naction names will be added to the props. If called with the special argument `'state'`, only the state\nwill be added to the props.\n\n```javascript\n// Props added to the component:\n// { state, ItemStore }\n@inject(app('ItemStore'))\n\n// Props added to the component:\n// { state, ItemStore, OtherStore }\n@inject(app())\n\n// Props added to the component:\n// { state }\n@inject(app('state'))\n```\n\nSo yeah, it might be a bad idea to call your store `state`. The special case was added because the main job of\nReact components is to render the current state as UI. To mutate the state is secondary and I want to give you\na way to prevent littering your component props.\n\nAlso, I highly recommend to capitalize the name of the stores. When developing, you may want to pass in props of the\nsame name as some of your stores. Normal `inject` rules apply, and your prop will overwrite what `app` wanted to\ninject.\n\n---\n\n### `collection()`\n\nThe collection action factory eases your interactions with observable collections. The factory takes an\nobservable array as its only argument and returns all collection actions.\n\nA collection is usually an array of objects, but `collection` will also, in most cases, support arrays\nof simple values.\n\n`const itemActions = collection(itemCollection)`\n\n##### `itemActions.setItems(items) =\u003e itemCollection`\nsetItems replaces the array with new items.\n\n##### `itemActions.addItems(items, unique = 'id', processAll) =\u003e [added items]`\naddItems adds new items into the collection. Give a property name as a string as the `unique` argument to\nmake that property value unique among all items in the collection. Pass `false` as the unique argument\nto disable the uniqueness check. The uniqueness check is on by default for the property `id`.\n\naddItems also takes a `processAll` argument. This is a function that will be called with the whole\ncollection, new items added, right before the old collection is replaced with it. This is an opportunity\nto sort the collection among other things.\n\n##### `itemActions.addItem(item, unique = 'id', replace = false, first = false) =\u003e item`\naddItem adds a single item to the collection. Unique checking is on by default for the `id` property.\n\nThe `replace` argument determines how the action behaves if an existing item is found in the collection\nwith the same property value unique checked for. Normally this will cause addItem to return without adding\nanything, but by setting this to  `true`, addItem will isntead replace the exisitng item with the new one.\n\nThe `first` argument decides if the new item should be added as the first element in the collection (using\nsplice) or, the default, as the last element in the collection (using push).\n\n##### `itemActions.updateItem(item, idProp = 'id') =\u003e item`\nupdateItem will use `extendObservable` with the new data on an existing item if found in the collection.\nUse the idProp argument to define how an existing item is matched.\n\n##### `itemActions.updateOrAdd(item, idProp = 'id', first = false) =\u003e item`\nThis action will first determine if the item exists in the collection. If it does, `updateItem` will be\ninvoked. If it doesn't, `addItem` will be invoked.\n\n##### `itemActions.removeItem(itemOrIndexOrId, idProp = 'id') =\u003e false || the removed item`\nremoveItem will cook dinner and play the ukulele for you.\n\nNo but seriously, it removes an item from the collection. It can take either an item, an index or an id.\nIf the first argument is a number, the item at that index will be removed. If it is a string, the item with\nthe idProp of the same value will be removed. If it is an object, the item will be found from the collection\nby the idProp and removed.\n\nPlease use a higher-order function to customize this behaviour if needed.\n\n##### `itemActions.clear(matcherFunction = false)`\nClears the collection completely if a matcher function is not passed. If a matcher is passed, each item the\nmatcher returns true for will be removed from the collection.\n\n---\n\n### `value()`\n\nThe value actions contain a few shortcuts for setting values, mainly so that you don't need to wrap your own\nactions with `action`. They are also quite convenient! To create value actions, pass in the state object AND\nthe name of the value these actions should concern as a string. You may also pass an initial value, that will\nbe assigned if you invoke the actions without arguments.\n\n`const valueActions = value(state, 'value', null)`\n\nThe property name will be used as a suffix for each action returned from the factory.\n\nAn example of computed action names:\n\n```javascript\nconst valueActions = value(state, 'derp') =\u003e { setDerp, extendDerp }\nconst valueActions = value(state, 'item') =\u003e { setItem, extendItem }\n```\n\nThis way you can make setter actions for many items in one store or action factory without them interfering\nwith each other.\n\n##### `valueActions.setValue(newValue = initial) =\u003e newValue`\nAssigns the new value to the property.\n\n##### `valueActions.extendValue(newValue = initial) =\u003e extendObservable(state.value, newValue)`\nExtends the new value onto a property of type object.\n\n---\n\nRoadmap\n-------\n\nThis is an initial release that only serves to get this idea out, because I think it is important.\nFor each new project I recreated a clunky state structure with classes until a lightbulb went off\nand I got excited for the idea of composition over inheritance and this state structure was born.\n\nI also think the lack of structure is a silent issue with mobx for new developers, This is unfair,\nbut comparisons to Redux are inevitably made. New developers are lost on how to structure their state,\nand seasoned mobx developers all have a different structure. There are many libraries on npm that bring\nstrcuture to mobx, but most are unmaintained. That is why mobx-app is more of a theory than a library.\nYou could easily implement it completely without even installing mobx-app! I just wanted a single source of\ntruth to the various helpers that I have deviced in various projects.\n\n### Next up:\n- Add tests (hey, this is only a quick and dirty braindump so far!)\n- Include more actions (mobx maps up next!)\n- Listen to feedback and evolve mobx-app!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanieldunderfelt%2Fmobx-app","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanieldunderfelt%2Fmobx-app","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanieldunderfelt%2Fmobx-app/lists"}