{"id":13499634,"url":"https://github.com/cs01/stator","last_synced_at":"2025-03-29T05:31:52.885Z","repository":{"id":57370238,"uuid":"121458158","full_name":"cs01/stator","owner":"cs01","description":"Simple, plain JavaScript state management with built-in support for React","archived":true,"fork":false,"pushed_at":"2018-06-18T07:23:45.000Z","size":839,"stargazers_count":2,"open_issues_count":5,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-10-29T00:14:25.324Z","etag":null,"topics":["javascript","react","state-management"],"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/cs01.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":"2018-02-14T01:36:32.000Z","updated_at":"2023-01-28T15:27:39.000Z","dependencies_parsed_at":"2022-09-13T02:53:13.383Z","dependency_job_id":null,"html_url":"https://github.com/cs01/stator","commit_stats":null,"previous_names":["cs01/statorgfc"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cs01%2Fstator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cs01%2Fstator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cs01%2Fstator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cs01%2Fstator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cs01","download_url":"https://codeload.github.com/cs01/stator/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221956546,"owners_count":16907480,"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","react","state-management"],"created_at":"2024-07-31T22:00:37.041Z","updated_at":"2024-10-31T18:30:52.598Z","avatar_url":"https://github.com/cs01.png","language":"JavaScript","readme":"# Stator \u0026middot; [![version](https://img.shields.io/badge/release-v0.1.6-blue.svg)](https://www.npmjs.com/package/statorgfc)  [![build status](https://travis-ci.org/cs01/stator.svg?branch=master)](https://travis-ci.org/cs01/statorgfc)\n\n\n### Install\n```\nyarn add statorgfc\n```\n\n**simple, plain JavaScript state management with built-in support for React.**\n\n![Image](https://github.com/cs01/statorgfc/raw/master/images/counter.png)\n[Try it Live](https://codesandbox.io/s/github/cs01/statorgfc/tree/master/examples/counter)\n\nStator works by adding methods around a global plain JavaScript object, and attaching callback functions to it. The callback functions automatically call `setState` on the Component(s) that need it. [Read more](https://medium.com/@grassfedcode/state-management-tools-for-a-react-powered-frontend-to-gdb-686db2d122a5).\n\nIt was developed as part of [gdbgui](https://github.com/cs01/gdbgui) and has recently been bundled into this library. gdbgui is 5749 lines of JavaScript in 41 files that create a frontend to a C/C++ debugger. It has many disparate components that need to update frequently and efficiently from websocket and AJAX data.\n\n### Create, Read, Update\nThis should look very similar to React's API.\n```js\nimport {store} from 'statorgfc'\n\nstore.initialize({count: 0})\n\nstore.get('count')  // 0\nstore.set({count: store.get('count') + 1}) // changed value to 1\nstore.get('count') // 1\n```\n\n### Subscribe a React Component to changes\nCall `store.connectComponentState()` in your constructor. That's it!\n```js\nimport React from 'react'\nimport {store} from 'statorgfc'\n\nstore.initialize({\n    numSheep: 10,\n    numWolves: 2,\n    numChickens: 90\n})\n\nclass SheepWatcher extends React.Component {\n  constructor(){\n    super()\n    // connect global store to the state of this component\n    // this.setState will be called when 'numSheep' or 'numWolves' changes\n    store.connectComponentState(this, ['numSheep', 'numWolves'])  \n  }\n  render(){\n    return {this.state.numSheep \u003e this.state.numWolves ? 'all good' : 'watch out sheep!'}\n  }\n}\n```\n\n```js\n// somewhere else in code far, far away...\nstore.set({numWolves: 15})  // SheepWatcher has setState() called\nstore.set({numSheep: store.get('numSheep') + 1})  // SheepWatcher has setState() called\nstore.set({numChickens: 100})  // No call to setState() in SheepWatcher\n// because sheepWatcher isn't connected to the numChickens key\n```\n\nThat's all you need to know to get started. There are a some other helper functions and options which you can read about below.\n\n\n#### View Subscribers for each key\n```js\nlet watchers = store.getKeySubscribers()\nconsole.log(watchers)\n// {\n//   \"numSheep\": [\"SheepWatcher\"],\n//   \"numWolves\": [\"SheepWatcher\"]\n// }\n```\n\nIf a key in the store is not being acted upon by anything, that is useful to know too. You can probably (but not definitely) remove that key from the global store and simply store it statically somewhere else.\n```js\nlet no_watchers = store.getUnwatchedKeys()\nconsole.log(no_watchers)\n// [\"numChickens\"]\n```\n\n### Use Middleware\n```js\nstore.use((key, oldval, newval)=\u003econsole.log(key, 'is about to change'))\n```\nRead more on middleware below and in the API documentation.\n\n### Subscribe a JavaScript function to changes\n```js\nimport {store} from 'statorgfc'\n\nstore.initialize({count: 0})\n\nstore.subscribe(()=\u003econsole.log('store changed!')) // call anytime something changes, and log entire store\nstore.set({count: store.get('count') + 1})  // prints {count: 1}\nstore.set({count: store.get('count')})  // no callbacks triggered, because the value didn't actually change\n```\n\nSee [more examples](https://github.com/cs01/statorgfc/tree/master/examples/).\n\n## When to Use\nUsed for state management of a JavaScript application when [any of the following](https://medium.com/@fastphrase/when-to-usoe-redux-f0aa70b5b1e2) are true\n* the same piece of application state needs to be mapped to multiple container components\n* there are global components that can be accessed from anywhere\n* too many props are being passed through multiple hierarchies of components\n\nIf you can maintain the state of a component within the component itself **you should not use statorgfc**.\n\n## Features:\n* **Global State**: State management occurs in one location -- a variable named *store*.\n* **Intuitive, Small API**: Similar to React's, with no boilerplate necessary\n* **Typesafe changes**: Values can only be replaced with data of the same type\n* **Immutable data... or not**: You choose if you want to work with immutable data structures by setting a single boolean\n* **Efficient**: If a value isn't changed during an update to the store, listeners aren't notified\n* **Optional Middleware**: Use or write middleware to perform arbitrary validation, logging, etc. before store's updates occur\n\n\n## Steps for Use\n1. Initialize once with `store.initialize({...})`\n2. Subscribe components to keys with `connectComponentState(this, ['key1', 'key2', ...])`\n3. Update the store with `store.set('key', val)` or `store.set({key: val})`. This should look familiar to React users.\n4. Read the store with `store.get('key')`\n5. Repeat step 3 and 4, and Components will automatically and efficiently re-render\n\nThere are some constraints built into Stator to ensure better usability.\n1. Keys cannot be added or removed after initialization\n1. Values must retain the same type during updates\n1. The store can only be updated with calls to `store.set()`.\n1. The store can only be read with calls to `store.get()`. This returns a copy of the value, so when you update that copy, it does not affect the underlying store until `store.set()` is called.\n\n\n## API\nMethods on store\n```js\nimport {store} from 'statorgfc'\n```\n### initialize\nInitializes store. The initial value must be a JavaScript object with key/value pairs. Keys cannot be added or removed, and the values must retain the same type during updates. Returns nothing.\n```js\nstore.initialize({key1: 'val', key2: 'val'})\n```\nOptions can also be set during initialization.\n```js\nstore.initialize({key1: 'val', key2: 'val'}, {immutable: false, debounce_ms: 10})\n```\n\n### set\nChange a value in the store. Similar to React's `setState`, this is the only way in which the store should be changed. Also similar to `setState`, an object can be passed in with the updated state. An error is thrown if the key does not already exist in the store.\n```js\nstore.set('mykey', newvalue)\nstore.set({mykey: newvalue})  // this syntax is okay too\n```\n\n### get\nGet value or reference to one of the keys in the store. If `store.options.immutable` is true, the value is returned. Otherwise a reference is returned.\n```js\nlet mykey = store.get('mykey')\nlet myotherkey = store.get('myotherkey')\nlet entire_store = store.get()  // with no arguments, whole store is returned\n// ...update entire_store\nstore.set(entire_store)\n```\n\n### connectComponentState\nConnect a react component's state to one or more keys in the store. Returns function that, when called, unsubscribes the Component from listening to those keys.\n```js\nconstructor(){\n  super()\n  this.unsubscribe = store.connectComponentState(this, ['showModal'])\n}\nrender(){\n  return this.state.showModal  // the Component's state automatically has the latest global value for all connected keys\n}\ncomponentWillUnmount(){\n  this.unsubscribe()\n}\n```\n### subscribeToKeys\nConnect a regular JavaScript function to a callback that is called only when one of a subset of the keys has been updated. Returns function that, when called, unsubscribes the function from listening to those keys.\n\n```js\nlet unsubscribe = store.subscribeToKeys(['myKey'], (keys_changed)=\u003econsole.log('myKey changed!'))\n```\n\n### getKeySubscribers\nReturn an object showing which keys are subscribed to by which functions and Components.\n```js\nlet watchers = store.getKeySubscribers()\nconsole.log(watchers)\n// {\n//   \"key1\": [\"MyComponent1\", \"MyComponent2\"],\n//   \"key2\": [\"MyComponent3\", \"my_vanilla_function\"]\n// }\n```\n\n### getUnwatchedKeys\nReturns an array of keys that do not trigger any callbacks when changed, and therefore may not need to be included in the global store.\n```js\nlet no_watchers = store.getUnwatchedKeys()\nconsole.log(no_watchers)\n// [\"key1\", \"key2\"]\n```\n\n### subscribe\nTrigger a callback function after the store changes its value for any key. Prefer `subscribeToKeys` over `subscribe` since callbacks will be triggered less often and therefore make your application more efficient.\n```js\nlet unsubscribe = store.subscribe(function(keys_changed){\n    console.log('keys', keys_changed, 'changed!')\n    console.log('The store is now:', store.get())\n  }\n)\n```\n### use\nUse a middleware function. If middleware functions returns true, the next middleware function will run until there are none left. At that point the store is updated and callbacks are dispatched. Otherwise, the middleware chain will stop, the store will not be updated, and callbacks will not be dispatched. Stator ships with some middleware, but any arbitrary function can be used.\n\nNote all middleware functions are called with the same arguments and must conform to this function signature.\n```js\nfunction logChanges(key, oldval, newval){\n  console.log(key, oldval, ' -\u003e ', newval)\n  return true\n}\nfunction persistToLocalStorage(key, oldval, newval){\n  localStorage.setItem(key, JSON.stringify(newval))\n  return true\n}\nfunction abortIfFive(key, oldval, newval){\n  if(newval === 5){\n    // user wants to change value to 5. Don't let this happen.\n    console.log('aborting!')\n    return false // returning false aborts the store update entirely!\n  }\n  return true\n}\nstore.use(logChanges)\nstore.use(abortIfFive)\nstore.use(persistToLocalStorage)\n\nstore.set('key', 3)  // \"key 0 -\u003e 3\", localStorage is set, store is updated.\nstore.set('key', 5)  // \"key 0 -\u003e 5\" \"aborting!\". store is not updated.\n```\n\n### options\nOptions are a plain JavaScript object with the following defaults:\n```js\noptions: {\n  // when calling store.get() returns copy if true, otherwise reference\n  immutable: true,\n  // time to delay before notifying subscribers (callbacks) of a change\n  debounce_ms: 0,\n},\n```\n\nThey can be set manually or during initialization:\n```js\nstore.options.immutable = false\nstore.options.debounce_ms = 10\n// or you can do this\nstore.initialize({key: 0}, {immutable: false, debounce_ms: 10})\n```\nThey should be set before using the store and never updated after that.\n\n## Built in Middleware\nStator comes with built in middleware. See [src/middleware.js](https://github.com/cs01/statorgfc/blob/master/src/middleware.js) for the full list of middleware functions.\n\nYou can use the built-in middleware like this:\n```js\nimport {store, middleware} from 'statorgfc'\n// ...initialize store\nstore.use(middleware.logChanges)\nstore.use(middleware.persistToLocalStorage)\n```\n\nAnd of course you can write your own as needed.\n\n## License\nMIT\n\n## Author\ngrassfedcode@gmail.com\n","funding_links":[],"categories":["Uncategorized"],"sub_categories":["Uncategorized"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcs01%2Fstator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcs01%2Fstator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcs01%2Fstator/lists"}