{"_id":"@dmrk/use-context-selector","_rev":"7-ff4f2cfa572955dfeac3631fec25a99b","name":"@dmrk/use-context-selector","dist-tags":{"latest":"0.1.6"},"versions":{"0.1.3":{"name":"@dmrk/use-context-selector","version":"0.1.3","keywords":["react","context","selectors"],"author":{"name":"Denis Marushchak","email":"denis@dmrk.dev"},"license":"ISC","_id":"@dmrk/use-context-selector@0.1.3","maintainers":[{"name":"dmarushchak","email":"denismar322@gmail.com"}],"homepage":"https://github.com/nova4u/use-context-selector","bugs":{"url":"https://github.com/nova4u/use-context-selector/issues"},"dist":{"shasum":"ef1dece4ba7d2851ecb69e6c2722da66c96416db","tarball":"https://registry.npmjs.org/@dmrk/use-context-selector/-/use-context-selector-0.1.3.tgz","fileCount":12,"integrity":"sha512-xuaFydjI6wyD5nicIzVfQVnbgmjm5KLHZo+0XwGZHXHq0QkFSrk18nlrLg+tyE/vZGOj76Pht1FEaFVn6IPltw==","signatures":[{"sig":"MEYCIQCfZ5eK4fUI5NBxyaYeCQQ/jqKkVT8FrSOyFb7egHBr0AIhAOsvw2rtNP5lh3H4cXqAmG0pdP+uKNb6PXnNQhgm2xYL","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":33755},"type":"module","_from":"file:dmrk-use-context-selector-0.1.3.tgz","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./shallow":{"types":"./dist/utils/shallow.d.ts","import":"./dist/shallow.js"}},"scripts":{"test":"vitest","build":"rollup -c rollup.config.mts","watch":"rollup -c -w rollup.config.mts","format":"prettier -c \"**/*.{ts,tsx,json,md}\"","version:bump":"node scripts/version-bump.js"},"_npmUser":{"name":"dmarushchak","email":"denismar322@gmail.com"},"_resolved":"/tmp/a8bbd02d42a86979d77ea3f84a38cbb6/dmrk-use-context-selector-0.1.3.tgz","_integrity":"sha512-xuaFydjI6wyD5nicIzVfQVnbgmjm5KLHZo+0XwGZHXHq0QkFSrk18nlrLg+tyE/vZGOj76Pht1FEaFVn6IPltw==","repository":{"url":"git+https://github.com/nova4u/use-context-selector.git","type":"git"},"_npmVersion":"11.6.1","description":"A library for using context with selectors","directories":{},"_nodeVersion":"24.11.0","_hasShrinkwrap":false,"devDependencies":{"tslib":"^2.8.1","rollup":"^4.52.5","vitest":"^4.0.7","prompts":"^2.4.2","prettier":"^3.6.2","happy-dom":"^20.0.10","@types/bun":"^1.3.2","typescript":"^5.9.3","@types/react":"^19.2.2","@testing-library/react":"^16.3.0","rollup-plugin-typescript2":"^0.36.0","@rollup/plugin-node-resolve":"^16.0.3"},"peerDependencies":{"react":">=18.0.0","react-dom":">=18.0.0"},"_npmOperationalInternal":{"tmp":"tmp/use-context-selector_0.1.3_1762785884649_0.19462415446482106","host":"s3://npm-registry-packages-npm-production"}},"0.1.4":{"name":"@dmrk/use-context-selector","version":"0.1.4","keywords":["react","context","selectors"],"author":{"name":"Denis Marushchak","email":"denis@dmrk.dev"},"license":"ISC","_id":"@dmrk/use-context-selector@0.1.4","maintainers":[{"name":"dmarushchak","email":"denismar322@gmail.com"}],"homepage":"https://github.com/nova4u/use-context-selector","bugs":{"url":"https://github.com/nova4u/use-context-selector/issues"},"dist":{"shasum":"625bf5fff59ccff0586a820d6df358a5695e98ff","tarball":"https://registry.npmjs.org/@dmrk/use-context-selector/-/use-context-selector-0.1.4.tgz","fileCount":12,"integrity":"sha512-fCIUktyVbIa+IbEA2WZGki5m4iIghQTZkNpptVyPzuiUrUvYDMjPytwlyLJDVEgqpe+9zi4IAVQFL1f+4OaOtQ==","signatures":[{"sig":"MEQCIFJeWiLlSfZmQcs1UWpg7cXxVkrIx1cgegmPPESicqeuAiBtsxgDiAWwneRn9bAXS/HQ+HBzhK4FTxU5lBHy9eMWjA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":33707},"type":"module","_from":"file:dmrk-use-context-selector-0.1.4.tgz","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./shallow":{"types":"./dist/utils/shallow.d.ts","import":"./dist/shallow.js"}},"scripts":{"test":"vitest","build":"rollup -c rollup.config.mts","watch":"rollup -c -w rollup.config.mts","format":"prettier -c \"**/*.{ts,tsx,json,md}\"","version:bump":"node scripts/version-bump.js"},"_npmUser":{"name":"dmarushchak","email":"denismar322@gmail.com"},"_resolved":"/tmp/931d4741c72b9388a0333c8e98fdaea8/dmrk-use-context-selector-0.1.4.tgz","_integrity":"sha512-fCIUktyVbIa+IbEA2WZGki5m4iIghQTZkNpptVyPzuiUrUvYDMjPytwlyLJDVEgqpe+9zi4IAVQFL1f+4OaOtQ==","repository":{"url":"git+https://github.com/nova4u/use-context-selector.git","type":"git"},"_npmVersion":"11.6.1","description":"A library for using context with selectors","directories":{},"_nodeVersion":"24.11.0","_hasShrinkwrap":false,"devDependencies":{"tslib":"^2.8.1","rollup":"^4.52.5","vitest":"^4.0.7","prompts":"^2.4.2","prettier":"^3.6.2","happy-dom":"^20.0.10","@types/bun":"^1.3.2","typescript":"^5.9.3","@types/react":"^19.2.2","@testing-library/react":"^16.3.0","rollup-plugin-typescript2":"^0.36.0","@rollup/plugin-node-resolve":"^16.0.3"},"peerDependencies":{"react":">=18.0.0","react-dom":">=18.0.0"},"_npmOperationalInternal":{"tmp":"tmp/use-context-selector_0.1.4_1762815370592_0.34402322994075707","host":"s3://npm-registry-packages-npm-production"}},"0.1.5":{"name":"@dmrk/use-context-selector","version":"0.1.5","keywords":["react","context","selectors"],"author":{"name":"Denis Marushchak","email":"denis@dmrk.dev"},"license":"ISC","_id":"@dmrk/use-context-selector@0.1.5","maintainers":[{"name":"dmarushchak","email":"denismar322@gmail.com"}],"homepage":"https://github.com/nova4u/use-context-selector","bugs":{"url":"https://github.com/nova4u/use-context-selector/issues"},"dist":{"shasum":"62f7ac6c055fa1c764685622a29a24f3ca5dba6e","tarball":"https://registry.npmjs.org/@dmrk/use-context-selector/-/use-context-selector-0.1.5.tgz","fileCount":12,"integrity":"sha512-fKx2a1gGVycet77TZlgK1QttHKXHy48wt49Mcc3cTxBw+WhjYDUMnITkgbHHljyBEdWvEl5SpvC/niJtf5Ijdg==","signatures":[{"sig":"MEQCIGjY3Z90Mr6xUZvSFSYFE2zmpC5d8qWSyBdpqpniA6lYAiBJEtYAtCJMIuJ0YF5EgTZPP7eBRdAXoZmofNLvajkNWg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":33742},"type":"module","_from":"file:dmrk-use-context-selector-0.1.5.tgz","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./shallow":{"types":"./dist/utils/shallow.d.ts","import":"./dist/shallow.js"}},"scripts":{"test":"vitest","build":"rollup -c rollup.config.mts","watch":"rollup -c -w rollup.config.mts","format":"prettier -c \"**/*.{ts,tsx,json,md}\"","version:bump":"node scripts/version-bump.ts"},"_npmUser":{"name":"dmarushchak","email":"denismar322@gmail.com"},"_resolved":"/tmp/b383adb31b2fd8091d287d223eb1ebb2/dmrk-use-context-selector-0.1.5.tgz","_integrity":"sha512-fKx2a1gGVycet77TZlgK1QttHKXHy48wt49Mcc3cTxBw+WhjYDUMnITkgbHHljyBEdWvEl5SpvC/niJtf5Ijdg==","repository":{"url":"git+https://github.com/nova4u/use-context-selector.git","type":"git"},"_npmVersion":"11.6.1","description":"A library for using context with selectors","directories":{},"_nodeVersion":"24.11.0","_hasShrinkwrap":false,"devDependencies":{"tslib":"^2.8.1","rollup":"^4.52.5","vitest":"^4.0.7","prompts":"^2.4.2","prettier":"^3.6.2","happy-dom":"^20.0.10","typescript":"^5.9.3","@types/node":"^24.10.1","@types/react":"^19.2.2","@types/prompts":"^2.4.9","@testing-library/react":"^16.3.0","rollup-plugin-typescript2":"^0.36.0","@rollup/plugin-node-resolve":"^16.0.3"},"peerDependencies":{"react":">=18.0.0","react-dom":">=18.0.0"},"_npmOperationalInternal":{"tmp":"tmp/use-context-selector_0.1.5_1762924748718_0.17036928996169065","host":"s3://npm-registry-packages-npm-production"}},"0.1.6":{"name":"@dmrk/use-context-selector","version":"0.1.6","author":{"name":"Denis Marushchak","email":"denis@dmrk.dev"},"repository":{"type":"git","url":"git+https://github.com/nova4u/use-context-selector.git"},"homepage":"https://github.com/nova4u/use-context-selector","keywords":["react","context","selectors","hooks","state-management","performance","typescript","react-context"],"description":"Lightweight React library for context with fine-grained selector-based subscriptions, preventing unnecessary re-renders","exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"},"./shallow":{"import":"./dist/shallow.js","types":"./dist/utils/shallow.d.ts"}},"type":"module","license":"ISC","peerDependencies":{"react":">=18.0.0","react-dom":">=18.0.0"},"devDependencies":{"@rollup/plugin-node-resolve":"^16.0.3","@testing-library/react":"^16.3.0","@types/node":"^24.10.1","@types/prompts":"^2.4.9","@types/react":"^19.2.2","happy-dom":"^20.0.10","prettier":"^3.6.2","prompts":"^2.4.2","rollup":"^4.52.5","rollup-plugin-typescript2":"^0.36.0","tslib":"^2.8.1","typescript":"^5.9.3","vitest":"^4.0.7"},"scripts":{"build":"rollup -c rollup.config.mts","watch":"rollup -c -w rollup.config.mts","format":"prettier -c \"**/*.{ts,tsx,json,md}\"","test":"vitest","version:bump":"node scripts/version-bump.ts"},"_id":"@dmrk/use-context-selector@0.1.6","bugs":{"url":"https://github.com/nova4u/use-context-selector/issues"},"_integrity":"sha512-onX0InBE8cWIxoW9c2eaPLhuzGayISnuPSwVaiMDhnUkcp64NtJKSECql9ZrKUSgWLas52Qksy2zlcVDcebZ3w==","_resolved":"/tmp/db6ad91c1e9002a2532e3d4eec9e688c/dmrk-use-context-selector-0.1.6.tgz","_from":"file:dmrk-use-context-selector-0.1.6.tgz","_nodeVersion":"24.11.0","_npmVersion":"11.6.1","dist":{"integrity":"sha512-onX0InBE8cWIxoW9c2eaPLhuzGayISnuPSwVaiMDhnUkcp64NtJKSECql9ZrKUSgWLas52Qksy2zlcVDcebZ3w==","shasum":"18ed0c3e060b53a0afc0d27d96ea4a19749a0679","tarball":"https://registry.npmjs.org/@dmrk/use-context-selector/-/use-context-selector-0.1.6.tgz","fileCount":12,"unpackedSize":33914,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQC2qEQqon7bcH8M2FIbR+kBq08C8nPc/6tdF4kbUKdJRAIhAN2V8vR5PbY8iV2GW5ygH5EOcPg1KpWTnXBV/MgvA7p4"}]},"_npmUser":{"name":"dmarushchak","email":"denismar322@gmail.com"},"directories":{},"maintainers":[{"name":"dmarushchak","email":"denismar322@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/use-context-selector_0.1.6_1762924940734_0.6766308852676266"},"_hasShrinkwrap":false}},"time":{"created":"2025-11-10T14:44:44.567Z","modified":"2025-11-12T05:22:21.112Z","0.1.0":"2025-11-10T12:16:00.248Z","0.1.1":"2025-11-10T12:24:28.729Z","0.1.3":"2025-11-10T14:44:44.841Z","0.1.4":"2025-11-10T22:56:10.786Z","0.1.5":"2025-11-12T05:19:08.883Z","0.1.6":"2025-11-12T05:22:20.897Z"},"bugs":{"url":"https://github.com/nova4u/use-context-selector/issues"},"author":{"name":"Denis Marushchak","email":"denis@dmrk.dev"},"license":"ISC","homepage":"https://github.com/nova4u/use-context-selector","keywords":["react","context","selectors","hooks","state-management","performance","typescript","react-context"],"repository":{"type":"git","url":"git+https://github.com/nova4u/use-context-selector.git"},"description":"Lightweight React library for context with fine-grained selector-based subscriptions, preventing unnecessary re-renders","maintainers":[{"name":"dmarushchak","email":"denismar322@gmail.com"}],"readme":"# use-context-selector\n\nA lightweight React library for creating context with fine-grained selector-based subscriptions, preventing unnecessary re-renders.\n\n## Table of Contents\n\n- [Installation](#installation)\n- [Quick Start](#quick-start)\n- [Performance Benefits](#performance-benefits)\n- [API Reference](#api-reference)\n  - [createStore](#createStorestateinitialvalue)\n  - [createUseStore](#createUseStorestore)\n  - [useStoreSelector](#useStoreSelectorcontext-selector-equalityfn)\n  - [InferState](#inferstatet)\n- [Advanced Usage](#advanced-usage)\n- [Best Practices](#best-practices)\n- [Common Pitfalls](#common-pitfalls)\n- [TypeScript Support](#typescript-support)\n- [Comparison with Other Solutions](#comparison-with-other-solutions)\n- [Requirements](#requirements)\n\n## Features\n\n- 🎯 **Selector-based subscriptions** - Components only re-render when their selected values change\n- ⚡ **Performance optimized** - Efficient updates with minimal re-renders\n- 🔒 **Type-safe** - Full TypeScript support with type inference\n- 🪶 **Lightweight** - Minimal dependencies, small bundle size\n- 🎨 **Simple API** - Easy to use with familiar React patterns\n- 🛠️ **Custom equality functions** - Use built-in `shallow` or any custom equality function (e.g., `zustand/shallow`)\n\n## Installation\n\n```bash\npnpm add @dmrk/use-context-selector\n```\n\n## Quick Start\n\n```tsx\nimport { createStore, createUseStore } from '@dmrk/use-context-selector';\n\n// 1. Create your store\nconst store = createStore({\n  count: 0,\n  name: 'John',\n});\n\n// 2. Create a custom hook (optional but recommended)\nconst useStore = createUseStore(store);\n\n// 3. Wrap your app with the Provider\nfunction App() {\n  return (\n    <store.Provider>\n      <Counter />\n      <Name />\n    </store.Provider>\n  );\n}\n\n// 4. Use selectors in your components\nfunction Counter() {\n  const count = useStore(state => state.count);\n  // ✅ Only re-renders when count changes\n  return (\n    <div>\n      <p>Count: {count}</p>\n      <button onClick={() => store.set({ count: count + 1 })}>Increment</button>\n    </div>\n  );\n}\n\nfunction Name() {\n  const name = useStore(state => state.name);\n  // ✅ Only re-renders when name changes\n  return <p>Name: {name}</p>;\n}\n\n// When you update count:\nstore.set({ count: 1 });\n// ✅ Only Counter re-renders, Name does not!\n```\n\n**Note:** For non-primitive selectors (objects/arrays), use a custom equality function like `shallow` or `zustand/shallow` to prevent unnecessary re-renders.\n\n## API Reference\n\n### `createStore<State>(initialValue)`\n\nCreates a new context store with the given initial state.\n\n**Parameters:**\n\n- `initialValue`: The initial state object\n\n**Returns:**\nAn object containing:\n\n- `Provider`: React component to wrap your app\n- `context`: The React context (for advanced usage)\n- `get()`: Function to get current state\n- `set(partial | updater)`: Function to update state. Accepts either:\n  - A partial state object: `set({ count: 10 })`\n  - An updater function: `set((state) => ({ count: state.count + 1 }))`\n- `subscribe()`: Function to subscribe to state changes (for advanced usage)\n\n**Example:**\n\n```tsx\nconst store = createStore({\n  user: { name: 'Alice', age: 25 },\n  theme: 'dark',\n});\n\n// Update with object\nstore.set({ theme: 'light' });\n\n// Update with function (useful when update depends on current state)\nstore.set(state => ({\n  user: { ...state.user, age: state.user.age + 1 },\n}));\n```\n\n### `createUseStore(store)`\n\nCreates a custom hook for the given store with a cleaner API.\n\n**Parameters:**\n\n- `store`: A store created by `createStore`\n\n**Returns:**\nA hook function that accepts:\n\n- `selector`: Function to select a value from state\n- `equalityFn` (optional): Custom equality function. Defaults to `Object.is`. Use `shallow` or `zustand/shallow` for objects/arrays.\n\n**Example:**\n\n```tsx\nimport shallow from '@dmrk/use-context-selector/shallow';\n\nconst useStore = createUseStore(store);\n\nfunction MyComponent() {\n  const userName = useStore(state => state.user.name);\n  const user = useStore(state => state.user, shallow);\n  return <div>{userName}</div>;\n}\n```\n\n### `useStoreSelector(context, selector, equalityFn?)`\n\nLow-level hook for using context with selectors. Use `createUseStore` for a better API.\n\n**Parameters:**\n\n- `context`: The React context from the store\n- `selector`: Function to select a value from state\n- `equalityFn` (optional): Custom equality function. Defaults to `Object.is`.\n\n**Returns:**\nThe selected value\n\n**Example:**\n\n```tsx\nimport shallow from '@dmrk/use-context-selector/shallow';\n\nfunction MyComponent() {\n  const count = useStoreSelector(store.context, state => state.count);\n  const user = useStoreSelector(store.context, state => state.user, shallow);\n  return <div>{count}</div>;\n}\n```\n\n### `InferState<T>`\n\nType utility to extract the State type from a store instance.\n\n```tsx\nimport type { InferState } from '@dmrk/use-context-selector';\n\nconst store = createStore({ count: 0, name: 'John' });\ntype StoreState = InferState<typeof store>; // { count: number; name: string }\n```\n\n## Advanced Usage\n\n### Derived Values\n\nYou can compute derived values in your selectors:\n\n```tsx\nconst useStore = createUseStore(store);\n\nfunction DoubledCounter() {\n  const doubled = useStore(state => state.count * 2);\n  return <div>Doubled: {doubled}</div>;\n}\n```\n\n### Complex Selectors\n\nSelect nested values or combine multiple values:\n\n```tsx\nconst store = createStore({\n  user: { name: 'Alice', age: 25 },\n  settings: { theme: 'dark', language: 'en' },\n});\n\nconst useStore = createUseStore(store);\n\nfunction UserInfo() {\n  const userAge = useStore(state => state.user.age);\n  const theme = useStore(state => state.settings.theme);\n\n  return <div className={theme}>User age: {userAge}</div>;\n}\n```\n\n### Updating State\n\nThe `set` method accepts either a partial state object or an updater function:\n\n```tsx\n// Partial updates\nstore.set({ count: 10 });\n\n// Function form (recommended when update depends on current state)\nstore.set(state => ({ count: state.count + 1 }));\n\n// Update from component\nfunction UpdateButton() {\n  const count = useStore(state => state.count);\n  return (\n    <button onClick={() => store.set(state => ({ count: state.count + 1 }))}>\n      Increment\n    </button>\n  );\n}\n```\n\n### Direct State Access\n\nAccess state directly without subscribing:\n\n```tsx\n// Get current state\nconst currentState = store.get();\n\n// Useful for event handlers or effects\nfunction handleClick() {\n  const current = store.get();\n  console.log('Current count:', current.count);\n  store.set({ count: current.count + 1 });\n}\n```\n\n## Best Practices\n\n- **Use equality functions for non-primitive selectors**: When selecting objects or arrays, use `shallow` or `zustand/shallow` to prevent unnecessary re-renders.\n- **Select specific values when possible**: Prefer selecting individual fields over entire objects when you only need a few values.\n\n## Common Pitfalls\n\n- **Returning new objects/arrays without equality function**: Selectors that return new references cause unnecessary re-renders. Use `shallow` or select individual values.\n\n## TypeScript Support\n\nFull type inference and type safety:\n\n```tsx\nconst store = createStore({\n  user: { name: 'Alice', age: 25 },\n  count: 0,\n});\n\nconst useStore = createUseStore(store);\n\nfunction MyComponent() {\n  // ✅ TypeScript infers return types\n  const userName = useStore(state => state.user.name); // string\n  const count = useStore(state => state.count); // number\n\n  // ❌ TypeScript error - invalid property\n  // const invalid = useStore(state => state.invalid);\n\n  return <div>{userName}</div>;\n}\n```\n\nUse `InferState<T>` to extract the state type from a store instance.\n\n## Comparison with Other Solutions\n\n### vs React Context + useContext\n\n**Standard Context:**\n\n- ❌ All consumers re-render on any state change\n- ❌ Requires manual optimization with useMemo/memo\n- ✅ Built into React\n\n**use-context-selector:**\n\n- ✅ Only re-renders when selected values change\n- ✅ Automatic optimization\n- ✅ Simpler API for complex state\n\n### vs Zustand\n\n**Zustand:**\n\n- ✅ More features (middleware, devtools, etc.)\n- ✅ Can be used outside React\n- ❌ Larger bundle size\n- ❌ More complex API\n\n**use-context-selector:**\n\n- ✅ Smaller bundle size\n- ✅ Simpler API\n- ✅ Better for React-only projects\n- ✅ Uses React Context (familiar pattern)\n- ❌ Fewer features (for now)\n\n## Requirements\n\n- React 18.0.0 or higher\n\n## License\n\nISC\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## Author\n\ndmrk\n","readmeFilename":"README.md"}