{"id":17657996,"url":"https://github.com/3rd/statelift","last_synced_at":"2025-07-29T17:08:07.024Z","repository":{"id":205458093,"uuid":"714123674","full_name":"3rd/statelift","owner":"3rd","description":"Minimalist proxy-based state management library for React (experimental).","archived":false,"fork":false,"pushed_at":"2023-12-18T19:22:49.000Z","size":210,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-26T21:23:17.121Z","etag":null,"topics":["react","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/3rd.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-11-04T01:29:36.000Z","updated_at":"2024-12-27T23:22:17.000Z","dependencies_parsed_at":"2023-12-05T08:26:11.211Z","dependency_job_id":"4678f1c2-1530-4049-a903-6464324c19a0","html_url":"https://github.com/3rd/statelift","commit_stats":null,"previous_names":["3rd/reactlift","3rd/statelift"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/3rd%2Fstatelift","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/3rd%2Fstatelift/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/3rd%2Fstatelift/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/3rd%2Fstatelift/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/3rd","download_url":"https://codeload.github.com/3rd/statelift/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248665156,"owners_count":21142117,"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":["react","typescript"],"created_at":"2024-10-23T14:43:52.087Z","updated_at":"2025-04-13T04:35:29.569Z","avatar_url":"https://github.com/3rd.png","language":"TypeScript","readme":"# Statelift\n\n**Statelift** is a proxy-based state management library for React.\n\\\nIts main purpose is to offer the simplest way of creating and using state containers.\n\n\u003e [!IMPORTANT]  \n\u003e Statelift is in a working but experimental state, proceed at your own peril.\n\u003e \\\n\u003e Don't put weird things in your store.\n\n**What it offers:**\n\n- The simplest API\n- Decent performance (benchmarks below)\n- Selectors\n- TypeScript all the way\n\n**How it looks:**\n\n```tsx\nimport { createStore, useStore } from \"statelift\";\n\n// This is just one of the signatures of createStore(), it can also just take an object,\n// or reference the store const directly, instead of the argument.\nconst store = createStore((root) =\u003e ({\n  foo: {\n    bar: 10,\n    baz: 5,\n  },\n  get doubleBar() {\n    return root.foo.bar * 2;\n  },\n  increaseBar() {\n    root.foo.bar += 10;\n  },\n}));\n\n// Bar only consumes store.doubleBar, and will rerender only when its dependencies change.\nconst Bar = () =\u003e {\n  const state = useStore(store);\n\n  return (\n    \u003c\u003e\n      \u003cp\u003eDouble bar: {state.doubleBar}\u003c/p\u003e\n      \u003cbutton onClick={() =\u003e state.foo.bar++}\u003eIncrease directly\u003c/button\u003e\n      \u003cbutton onClick={state.increaseBar}\u003eIncrease through action\u003c/button\u003e\n    \u003c/\u003e\n  );\n};\n\n// Same for Baz, it won't rerender when anything except foo.baz changes.\nconst Baz = () =\u003e {\n  const state = useStore(store);\n\n  return \u003cp\u003eBaz: {state.foo.baz}\u003c/p\u003e;\n};\n\n// You can interact with the store from anywhere you like, and it will work as you expect.\nstore.state.foo.bar = 100;\n```\n\n---\n\n### Usage\n\n#### Installation\n\n:point_right: [statelift on NPM](http://npmjs.com/package/statelift)\n\\\n`pnpm add statelift`\n\n#### Creating a store\n\nThe `createStore()` function takes either a plain object, or a builder function.\n\nIf you want to have getters (computed values) or functions that modify the state (actions) that live on\nbranches that are unreacheable from their local scope, you can refer to the root using either the **store variable\nitself**, or the **root argument** provided to the builder function.\n\n**Creating a store from an object**\n\nYou can always read and mutate data that is reacheable the local scope using `this`, it works as expected.\n\\\nChances are that this won't be very comfy, so using the builder function is probably what you want.\n\n```ts\nconst store = createStore({\n  nested: {\n    a: 10,\n    b: 5,\n  },\n  get sum() {\n    // \"this\" is \"store.state\" here, because we're at the root\n    return this.nested.a + this.nested.b;\n  },\n  increaseA(amount: number) {\n    this.nested.a += amount;\n  },\n  yummy: {\n    get doubleA() {\n      // \"this\" can't work here\n      return store.state.nested.a * 2;\n    },\n  },\n});\n```\n\n**Creating a store from a builder function**\n\nYour builder function is called with a reference to the root state.\n\\\nBasically `root` is `store.state`, and also `this` at the top level of the object you're returning.\n\\\nAnd yes, you're writing a function that takes its own return value as its argument.\n\n```ts\nconst store = createStore((root) =\u003e ({\n  nested: {\n    a: 10,\n    b: 5,\n  },\n  get sum() {\n    return root.nested.a + root.nested.b;\n  },\n  increaseA(amount: number) {\n    root.nested.a += amount;\n  },\n  yummy: {\n    get doubleA() {\n      return root.nested.a * 2;\n    },\n  },\n}));\n```\n\n#### Using a store\n\nAll you have to do to use your store in a React component is to pass it to the `useStore()` hook.\n\n```tsx\nconst counterStore = createStore({\n  value: 0,\n  increment() {\n    this.value++;\n  },\n});\n\nconst Component = () =\u003e {\n  const counter = useStore(counterStore);\n\n  return (\n    \u003c\u003e\n      \u003cp\u003eCount: {counter.value}\u003c/p\u003e\n      \u003cbutton onClick={() =\u003e counter.value++}\u003eIncrease (direct)\u003c/button\u003e\n      \u003cbutton onClick={counter.increment}\u003eIncrease (action)\u003c/button\u003e\n    \u003c/\u003e\n  );\n};\n```\n\n**Selectors**\n\nSometimes we need selectors to avoid rerendering a component depending on some per-item condition.\n\nFor example when having a list of items, of which one can be selected.\n\\\nWithout a selector, all the item components will be subscribed to the currently selected item identifier, and\nwhen that one changes, we'd rerender everything.\n\nTo avoid that, we can use a selector that runs at store level, and the component will only update when the\ncomputed value changes.\n\n```tsx\ntype Item = { id: string; label: string };\n\ntype Store = {\n  selectedId: string | null;\n  items: Item[];\n};\n\nconst store = createStore\u003cStore\u003e({\n  selectedId: null,\n  items: [\n    /* ...many items */\n  ],\n});\n\nconst ListItem = (item: Item) =\u003e {\n  const isSelected = useStore(store, (state) =\u003e state.selectedId === item.id);\n\n  return (\n    \u003cli\u003e\n      {isSelected \u0026\u0026 \u003cstrong\u003e*\u003c/strong\u003e}\n      {item.label}\n    \u003c/li\u003e\n  );\n};\n\nconst List = (items: Item[]) =\u003e {\n  const state = useStore(store);\n\n  return (\n    \u003cul\u003e\n      {state.items.map((item) =\u003e (\n        \u003cListItem key={item.id} item={item} /\u003e\n      ))}\n    \u003c/ul\u003e\n  );\n};\n```\n\n### Benchmarks\n\n![image](https://github.com/3rd/statelift/assets/59587503/eb09b938-3bfe-4283-8f46-ac14dd572da8)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F3rd%2Fstatelift","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F3rd%2Fstatelift","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F3rd%2Fstatelift/lists"}