{"id":24084562,"url":"https://github.com/oleystack/state","last_synced_at":"2025-04-30T20:47:11.354Z","repository":{"id":40387842,"uuid":"475177216","full_name":"oleystack/state","owner":"oleystack","description":"🚀 Tiny and powerful React hook-based state management library.","archived":false,"fork":false,"pushed_at":"2023-03-15T00:32:43.000Z","size":5903,"stargazers_count":55,"open_issues_count":3,"forks_count":2,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-10T02:42:56.948Z","etag":null,"topics":["constate","context-api","hook","hooks","mobx","react","react-hooks","react-native","reactjs","redux","state","state-management"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/@bit-about/state","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/oleystack.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null},"funding":{"github":["Gareneye"]}},"created_at":"2022-03-28T21:00:06.000Z","updated_at":"2024-02-06T00:32:49.000Z","dependencies_parsed_at":"2023-07-25T20:49:58.457Z","dependency_job_id":null,"html_url":"https://github.com/oleystack/state","commit_stats":null,"previous_names":["bit-about/state"],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oleystack%2Fstate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oleystack%2Fstate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oleystack%2Fstate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oleystack%2Fstate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/oleystack","download_url":"https://codeload.github.com/oleystack/state/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251780428,"owners_count":21642772,"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":["constate","context-api","hook","hooks","mobx","react","react-hooks","react-native","reactjs","redux","state","state-management"],"created_at":"2025-01-10T00:37:58.267Z","updated_at":"2025-04-30T20:47:11.301Z","avatar_url":"https://github.com/oleystack.png","language":"TypeScript","funding_links":["https://github.com/sponsors/Gareneye","https://github.com/sponsors/macoley"],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n\u003cimg alt=\"\" src=\"https://user-images.githubusercontent.com/1496580/162103874-f2fbde4b-f985-4c33-ac38-9d5d3b4ee37e.png\" /\u003e\u003cbr/\u003e\u003cbr/\u003e\n\u003ca href=\"https://www.npmjs.com/package/@bit-about/state\"\u003e\u003cimg alt=\"\" src=\"https://img.shields.io/npm/v/@bit-about/state.svg\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://bundlephobia.com/package/@bit-about/state\"\u003e\u003cimg alt=\"Bundle size\" src=\"https://img.shields.io/bundlephobia/minzip/@bit-about/state?label=size\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://codecov.io/gh/bit-about/state\"\u003e\u003cimg alt=\"\" src=\"https://img.shields.io/codecov/c/github/bit-about/state?token=BuGi92VqnL\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n## Install\n\n```bash\nnpm i @bit-about/state\n```\n\n## Features\n\n- 100% **Idiomatic React**\n- 100% Typescript with state types deduction\n- Efficient **sub-states selectors**\n- Get state from a hook...\n- ...or utilise static access\n- No centralized state provider\n- Tiny - only **1.4kB**\n- **Just works** ™\n\n### ➡️ [Check demo](https://bit-about.github.io/state/)\n\n## Usage\n\n```tsx\nimport { useState } from 'react'\nimport { state } from '@bit-about/state'\n\n// 1️⃣ Create a hook-based store\nconst [Provider, useStore] = state(() =\u003e {\n  const [alice, setAlice] = useState('Alice')\n  const [bob, setBob] = useState('Bob')\n  \n  return { alice, setAlice, bob, setBob }\n})\n\n// 2️⃣ Wrap tree with Provider\nconst App = () =\u003e (\n  \u003cProvider\u003e\n    \u003cChild /\u003e\n  \u003c/Provider\u003e\n)\n```\n\nand then\n```tsx\n// 3️⃣ Use the selector hook in component\nconst Child = () =\u003e {\n  const alice = useStore(state =\u003e state.alice)\n  \n  return \u003cp\u003e{alice}\u003c/p\u003e\n}\n```\n\n## State selectors\n\nAccess fine-grained control to the specific part of your state to re-render **only when necessary**.\n\n```tsx\n// 👍 Re-render when anything changed\nconst { alice, bob } = useStore()\n\n// 💪 Re-render when alice changed\nconst alice = useStore(state =\u003e state.alice)\n\n// 🤌 Re-render when alice or bob changed\nconst [alice, bob] = useStore(state =\u003e [state.alice, state.bob])\n\n// or\nconst { alice, bob } = useStore( \n  state =\u003e ({ alice: state.alice, bob: state.bob }) \n)\n```\n\n\u003e NOTE: **Values** in objects and arrays created on the fly are shallow compared.\n\n## Static store\n\nThe third element of the `state()` result tuple is a `store` object. Store is a static helper which provides access to the state **without a hook**.\n\n```tsx\nconst [Provider, useStore, store] = state(/* ... */)\n```\n\nand then\n```tsx\n// 👍 Get whole state\nconst { alice } = store.get()\n\n// 💪 Get substate\nconst alice = store\n  .select(state =\u003e state.alice)\n  .get()\n\n// 🤌 Subscribe to the store and listen for changes\nconst subscription = store\n  .select(state =\u003e state.alice)\n  .subscribe(alice =\u003e console.log(alice))\n  \n// remember to unsubscribe!\nsubscription.unsubscribe()\n```\n\n## State props\n\nThe state hook allows you to pass any arguments into the context. It can be some initial state or you could even return it and pass it through to the components. All state prop changes will update the context and trigger component re-rendering **only when necessary**.\n\n```tsx\nimport { useState } from 'react'\nimport { getUserById } from '../utils'\n\nconst [UserProvider, useUser] = state(props =\u003e {\n  const [user] = useState(() =\u003e getUserById(props.id))\n  return user\n})\n\nconst UserProfile = () =\u003e (\n  \u003cUserProvider id={2137}\u003e\n    {/* ... */}\n  \u003c/UserProvider\u003e\n)\n```\n\n## 👉 Functions in state\n\nPlease remember that functions defined without `React.useCallback` create themselves from scratch every time - which results in incorrect comparisons and components think the state has changed so they re-render themselves.\n\n```tsx\nimport { useState, useCallback } from 'react'\n\nconst [Provider, useStore] = state(() =\u003e {\n  const [counter, setCounter] = useState(0);\n   \n  // ✖️ It will re-render components every time\n  // const incrementCounter = () =\u003e setCounter(value =\u003e value + 1)\n\n  const incrementCounter = useCallback(\n    () =\u003e setCounter(value =\u003e value + 1),\n    [setCounter]\n  )\n\n  return { counter, incrementCounter }\n})\n```\n\n## BitAboutState 💛 [BitAboutEvent](https://github.com/bit-about/event)\n\nAre you tired of sending logic to the related components?\u003cbr /\u003e\nMove your bussiness logic to the hook-based state using `@bit-about/state` + `@bit-about/event`.\u003cbr /\u003e\n\nNow you've got **completely type-safe side-effects**. Isn't that cool?\n\n```tsx\nimport { useState } from 'react'\nimport { state } from '@bit-about/state'\nimport { useEvent } from './auth-events' // @bit-about/event hook\nimport User from '../models/user'\n\nconst [UserProvider, useUser] = state(() =\u003e {\n  const [user, setUser] = useState\u003cUser | null\u003e(null)\n    \n  useEvent({\n    userLogged: (user: User) =\u003e setUser(user),\n    userLoggout: () =\u003e setUser(null)\n  })\n    \n  return user\n})\n```\n\n## BitAboutState 💛 [React Query](https://github.com/tannerlinsley/react-query)\n\n```tsx\nimport { useQuery } from 'react-query'\nimport { fetchUser } from './user'\n\nconst useUserQuery = (id) =\u003e useQuery(['user', id], () =\u003e fetchUser(id))\n\nconst [UserProvider, useUser] = state(props =\u003e {\n  const { data: user } = useUserQuery(props.id)\n  return user\n})\n\nconst UserProfile = () =\u003e (\n  \u003cUserProvider id={2137}\u003e\n    {/* ... */}\n  \u003c/UserProvider\u003e\n)\n\n// 🧠 Re-render ONLY when user avatar changed (no matter if isLoading changes)\nconst avatar = useUser(state =\u003e state.user.avatar)\n```\n\n## Partners  \n\u003ca href=\"https://www.wayfdigital.com/\"\u003e\u003cimg alt=\"wayfdigital.com\" width=\"100\" height=\"100\" src=\"https://user-images.githubusercontent.com/1496580/161037415-0503f763-a60b-4d40-af9f-95d1304fa486.png\"/\u003e\u003c/a\u003e\n\n## Credits\n- [Constate](https://github.com/diegohaz/constate) - approach main inspiration\n- [use-context-selector](https://github.com/dai-shi/use-context-selector) \u0026 [FluentUI](https://github.com/microsoft/fluentui) - fancy re-render avoiding tricks and code main inspiration\n\n## License\nMIT © [Maciej Olejnik 🇵🇱](https://github.com/macoley)\n\n## Support me \n\n\u003ca href=\"https://github.com/sponsors/macoley\"\u003e\u003cimg alt=\"Support me!\" src=\"https://img.shields.io/badge/github.com-Support%20me!-green\"/\u003e\u003c/a\u003e\n\nIf you use my library and you like it...\u003cbr /\u003e\nit would be nice if you put the name `BitAboutState` in the work experience section of your resume.\u003cbr /\u003e\nThanks 🙇🏻! \n\n\n---\n\u003cp align=\"center\"\u003e🇺🇦 Slava Ukraini\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foleystack%2Fstate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foleystack%2Fstate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foleystack%2Fstate/lists"}