{"id":15379073,"url":"https://github.com/hmans/statery","last_synced_at":"2025-04-13T21:41:17.711Z","repository":{"id":45928392,"uuid":"322029626","full_name":"hmans/statery","owner":"hmans","description":"Surprise-Free State Management! Designed for React with functional components, but can also be used with other frameworks (or no framework at all.)","archived":false,"fork":false,"pushed_at":"2023-08-30T17:42:26.000Z","size":505,"stargazers_count":75,"open_issues_count":1,"forks_count":6,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-11T14:27:40.774Z","etag":null,"topics":["hooks","react","state-management"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/hmans.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-12-16T15:51:04.000Z","updated_at":"2025-02-27T04:21:19.000Z","dependencies_parsed_at":"2024-06-18T21:40:31.124Z","dependency_job_id":null,"html_url":"https://github.com/hmans/statery","commit_stats":{"total_commits":176,"total_committers":5,"mean_commits":35.2,"dds":0.07386363636363635,"last_synced_commit":"ec690db74d667e9e48381d9eba61e728aa582da6"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hmans%2Fstatery","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hmans%2Fstatery/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hmans%2Fstatery/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hmans%2Fstatery/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hmans","download_url":"https://codeload.github.com/hmans/statery/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248788864,"owners_count":21161726,"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":["hooks","react","state-management"],"created_at":"2024-10-01T14:18:14.571Z","updated_at":"2025-04-13T21:41:17.685Z","avatar_url":"https://github.com/hmans.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"```\n                                          💥           ⭐️            💥      ✨\n                             ✨                                ✨\n  ███████╗████████╗ █████╗ ████████╗███████╗██████╗ ██╗   ██╗██╗   ✨\n  ██╔════╝╚══██╔══╝██╔══██╗╚══██╔══╝██╔════╝██╔══██╗╚██╗ ██╔╝██║\n  ███████╗   ██║   ███████║   ██║   █████╗  ██████╔╝ ╚████╔╝ ██║     ⭐️    ✨\n  ╚════██║   ██║   ██╔══██║   ██║   ██╔══╝  ██╔══██╗  ╚██╔╝  ╚═╝\n  ███████║   ██║   ██║  ██║   ██║   ███████╗██║  ██║   ██║   ██╗     ✨  💥\n  ╚══════╝   ╚═╝   ╚═╝  ╚═╝   ╚═╝   ╚══════╝╚═╝  ╚═╝   ╚═╝   ╚═╝        ✨\n                                         ✨                 ✨   ⭐️\n  SURPRISE-FREE STATE MANAGEMENT!                💥\n                                                              ✨\n```\n\n[![Version](https://img.shields.io/npm/v/statery?style=for-the-badge)](https://www.npmjs.com/package/statery)\n![CI](https://img.shields.io/github/actions/workflow/status/hmans/statery/tests.yml?style=for-the-badge)\n[![Downloads](https://img.shields.io/npm/dt/statery.svg?style=for-the-badge)](https://www.npmjs.com/package/statery)\n[![Bundle Size](https://img.shields.io/bundlephobia/min/statery?label=bundle%20size\u0026style=for-the-badge)](https://bundlephobia.com/result?p=statery)\n\n### Features 🎉\n\n- Simple, **noise- and surprise-free API**. Check out the [demo]!\n- **Extremely compact**, both in bundle size as well as API surface (2 exported functions!)\n- Fully **tested**, fully **typed**!\n- **Designed for React** (with functional components and hooks), but can also be used **without it**, or with other frameworks (but you may need to bring your own glue.)\n\n### Non-Features 🧤\n\n- Doesn't use **React Context** (but you can easily use it to provide a context-specific store!)\n- Provides a simple `set` function for updating a store and not much else (have you checked out the [demo] yet?) If you want to use **reducers** or libraries like [Immer], these can easily sit on top of your Statery store.\n- Currently no support for (or concept of) **middlewares**, but this may change in the future.\n- While the `useStore` hook makes use of **proxies**, the store contents themselves are never wrapped in proxy objects. (If you're looking for a fully proxy-based solution, I recommend [Valtio].)\n- **React Class Components** are not supported (but PRs welcome!)\n\n## SUMMARY\n\n```tsx\nimport { makeStore, useStore } from \"statery\"\n\nconst store = makeStore({ counter: 0 })\n\nconst increment = () =\u003e\n  store.set((state) =\u003e ({\n    counter: state.counter + 1\n  }))\n\nconst Counter = () =\u003e {\n  const { counter } = useStore(store)\n\n  return (\n    \u003cdiv\u003e\n      \u003cp\u003eCounter: {counter}\u003c/p\u003e\n      \u003cbutton onClick={increment}\u003eIncrement\u003c/button\u003e\n    \u003c/div\u003e\n  )\n}\n```\n\nFor a more fully-featured example, please check out the [demo].\n\n## BASIC USAGE\n\n### Adding it to your Project\n\n```\nnpm add statery\nyarn add statery\npnpm add statery\n```\n\n### Creating a Store\n\nStatery stores wrap around plain old JavaScript objects that are free to contain any kind of data:\n\n```ts\nimport { value makeStore } from \"statery\"\n\nconst store = makeStore({\n  player: {\n    id: 1,\n    name: \"John Doe\",\n    email: \"john@doe.com\"\n  },\n  gold: 100,\n  wood: 0,\n  houses: 0\n})\n```\n\nIf you're using TypeScript, the type of the store state will be inferred from the initial state; but you may also pass a type argument to `makeStore` to explicitly set the type of the store:\n\n```ts\nconst store = makeStore\u003c{ count: number }\u003e({ count: 0 })\n```\n\n### Updating the Store\n\nThe store object's `set` function will update the store's state and notify any listeners who have subscribed to changes:\n\n```tsx\nconst collectWood = () =\u003e\n  store.set((state) =\u003e ({\n    wood: state.wood + 1\n  }))\n\nconst buildHouse = () =\u003e\n  store.set((state) =\u003e ({\n    wood: state.wood - 10,\n    houses: state.houses + 1\n  }))\n\nconst Buttons = () =\u003e {\n  return (\n    \u003cp\u003e\n      \u003cbutton onClick={collectWood}\u003eCollect Wood\u003c/button\u003e\n      \u003cbutton onClick={buildHouse}\u003eBuild House\u003c/button\u003e\n    \u003c/p\u003e\n  )\n}\n```\n\nUpdates will be shallow-merged with the current state, meaning that top-level properties will be replaced, and properties you don't update will not be touched.\n\n### Reading from a Store (with React)\n\nWithin a React component, use the `useStore` hook to read data from the store:\n\n```tsx\nimport { value useStore } from \"statery\"\n\nconst Wood = () =\u003e {\n  const { wood } = useStore(store)\n\n  return \u003cp\u003eWood: {wood}\u003c/p\u003e\n}\n```\n\nWhen any of the store's properties that your component accesses are updated, they will automatically re-render, automatically receiving the new state.\n\n### Reading from a Store (without React)\n\nA Statery store provides access to its current state through its `state` property:\n\n```ts\nconst store = makeStore({ count: 0 })\nconsole.log(store.state.count)\n```\n\nYou can also imperatively [subscribe to updates](#subscribing-to-updates-imperatively).\n\n## ADVANCED USAGE\n\n### Deriving Values from a Store\n\nJust like mutations, functions that derive values from the store's state can be written as standalone functions:\n\n```tsx\nconst canBuyHouse = ({ wood, gold }) =\u003e wood \u003e= 5 \u0026\u0026 gold \u003e= 5\n```\n\nThese will work from within plain imperative JavaScript code...\n\n```tsx\nif (canBuyHouse(store.state)) {\n  console.log(\"Let's buy a house!\")\n}\n```\n\n...mutation code...\n\n```tsx\nconst buyHouse = () =\u003e\n  store.set((state) =\u003e\n    canBuyHouse(state)\n      ? {\n          wood: state.wood - 5,\n          gold: state.gold - 5,\n          houses: state.houses + 1\n        }\n      : {}\n  )\n```\n\n...as well as React components, which will automatically be re-rendered if any of the underlying data changes:\n\n```tsx\nconst BuyHouseButton = () =\u003e {\n  const store = useStore(store)\n\n  return (\n    \u003cbutton onClick={buyHouse} disabled={!canBuyHouse(store)}\u003e\n      Buy House (5g, 5w)\n    \u003c/button\u003e\n  )\n}\n```\n\n### Forcing a store update\n\nWhen the store is updated, Statery will check which of the properties within the update object are actually different objects (or scalar values) from the previous state, and will only notify listeners to those properties.\n\nIn some cases, you may want to force a store update even though the property has not changed to a new object. For these situations, the `set` function allows you to pass a second argument; if this is set to `true`, Statery will ignore the equality check and notify all listeners to the properties included in the update.\n\nExample:\n\n```tsx\nconst store = makeStore({\n  rotation: new THREE.Vector3()\n})\n\nexport const randomizeRotation = () =\u003e\n  store.set(\n    (state) =\u003e ({\n      rotation: state.rotation.randomRotation()\n    }),\n    true\n  )\n```\n\n### Subscribing to updates (imperatively)\n\nUse a store's `subscribe` function to register a callback that will be executed every time the store is changed.\nThe callback will receive both an object containing of the changes, as well as the store's current state.\n\n```ts\nconst store = makeStore({ count: 0 })\n\nconst listener = (changes, state) =\u003e {\n  console.log(\"Applying changes:\", changes)\n}\n\nstore.subscribe(console.log)\nstore.set((state) =\u003e ({ count: state.count + 1 }))\nstore.unsubscribe(console.log)\n```\n\n## NOTES\n\n### Stuff that probably needs work\n\n- [ ] No support for middleware yet. Haven't decided on an API that is adequately simple.\n\n### Prior Art \u0026 Credits\n\nStatery was born after spending a lot of time with the excellent state management libraries provided by the [Poimandres](https://github.com/pmndrs) collective, [Zustand] and [Valtio]. Statery started out as an almost-clone of Zustand, but with the aim of providing an even simpler API. The `useStore` hook API was inspired by Valtio's very nice `useProxy`.\n\nStatery is written and maintained by [Hendrik Mans](https://www.hmans.dev/).\n\n[demo]: https://codesandbox.io/s/statery-clicker-game-hjxk3?file=/src/App.tsx\n[zustand]: https://github.com/pmndrs/zustand\n[valtio]: https://github.com/pmndrs/valtio\n[immer]: https://github.com/immerjs/immer\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhmans%2Fstatery","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhmans%2Fstatery","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhmans%2Fstatery/lists"}