{"_id":"@blumintinc/use-context-selector","_rev":"3-6d95d530ba3c9b264226e98855d0cadd","name":"@blumintinc/use-context-selector","dist-tags":{"latest":"2.0.0"},"versions":{"2.0.0":{"name":"@blumintinc/use-context-selector","version":"2.0.0","keywords":["react","context","hooks","useContextSelector","shallow-comparison","blumintinc","performance","optimization"],"author":{"name":"BluMintInc"},"license":"MIT","_id":"@blumintinc/use-context-selector@2.0.0","maintainers":[{"name":"oconnorjoseph","email":"oconnor.joseph@columbia.edu"},{"name":"brodie-m","email":"brodiedmcguire@gmail.com"}],"contributors":[{"url":"original author","name":"Daishi Kato"},{"name":"BluMintInc Team"}],"homepage":"https://github.com/BluMintInc/use-context-selector#readme","bugs":{"url":"https://github.com/BluMintInc/use-context-selector/issues"},"dist":{"shasum":"9fb1eb5bb1e9ceea45592b99cb74573509ec7889","tarball":"https://registry.npmjs.org/@blumintinc/use-context-selector/-/use-context-selector-2.0.0.tgz","fileCount":9,"integrity":"sha512-9FmeOSA42FMwCR3xTnXl+WaGlcbsgw24ErS233m04HtJpdy3MiWrwSaGC8eQmzOA4Tkc1wKKbkGfoD7OiUpaVQ==","signatures":[{"sig":"MEUCIQD73mq4lxfwFRogtqHySXEEcCctHBbJDF+M8MNfR05KTwIgUWBNXlrtvlsBymL4JUkndUeqiFKMs/WWNdF2JxAtBcw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":49370},"main":"./dist/index.js","type":"module","_from":"file:blumintinc-use-context-selector-2.0.0.tgz","types":"./dist/index.d.ts","source":"./src/index.ts","exports":{".":{"default":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/cjs/index.d.ts","default":"./dist/cjs/index.js"}},"./package.json":"./package.json"},"scripts":{"test":"pnpm run '/^test:.*/'","apidoc":"documentation readme src --section API --markdown-toc false --parse-extension ts","compile":"rm -rf dist && pnpm run '/^compile:.*/'","test:lint":"eslint .","test:spec":"vitest run","test:types":"tsc -p . --noEmit","compile:cjs":"tsc -p tsconfig.cjs.json && echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json","compile:esm":"tsc -p tsconfig.esm.json","test:format":"prettier -c .","examples:02_person":"DIR=02_person vite","examples:01_counter":"DIR=01_counter vite","test:types:examples":"tsc -p examples --noEmit","examples:03_suspense":"DIR=03_suspense vite"},"_npmUser":{"name":"oconnorjoseph","email":"oconnor.joseph@columbia.edu"},"prettier":{"singleQuote":true},"_resolved":"/private/var/folders/8j/8gsxmbws0fgdj7c946y_tf_c0000gn/T/55605bc353eca8de2bb6a2a3df8c9762/blumintinc-use-context-selector-2.0.0.tgz","_integrity":"sha512-9FmeOSA42FMwCR3xTnXl+WaGlcbsgw24ErS233m04HtJpdy3MiWrwSaGC8eQmzOA4Tkc1wKKbkGfoD7OiUpaVQ==","repository":{"url":"git+https://github.com/BluMintInc/use-context-selector.git","type":"git"},"_npmVersion":"10.8.2","description":"React useContextSelector hook with shallow object comparison - BluMintInc fork","directories":{},"sideEffects":false,"_nodeVersion":"22.5.1","publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.0.11","react":"^18.3.1","eslint":"^9.19.0","vitest":"^3.0.4","prettier":"^3.4.2","happy-dom":"^16.7.2","react-dom":"^18.3.1","scheduler":"^0.25.0","ts-expect":"^1.3.0","@eslint/js":"^9.19.0","typescript":"^5.7.3","@types/node":"^22.10.10","@types/react":"^19.0.8","documentation":"^14.0.3","@types/react-dom":"^19.0.3","@types/scheduler":"^0.23.0","typescript-eslint":"^8.21.0","eslint-plugin-react":"^7.37.4","vite-tsconfig-paths":"^5.1.4","eslint-plugin-import":"^2.31.0","use-context-selector":"link:","@testing-library/react":"^16.2.0","eslint-plugin-jsx-a11y":"^6.10.2","@testing-library/jest-dom":"^6.6.3","eslint-plugin-react-hooks":"6.0.0-rc.1","@testing-library/user-event":"^14.6.1","eslint-import-resolver-typescript":"^3.7.0"},"peerDependencies":{"react":">=18.0.0","scheduler":">=0.19.0"},"_npmOperationalInternal":{"tmp":"tmp/use-context-selector_2.0.0_1763151307822_0.8050371294060543","host":"s3://npm-registry-packages-npm-production"}}},"time":{"created":"2025-11-14T20:15:07.731Z","modified":"2026-04-29T01:17:40.403Z","2.0.0":"2025-11-14T20:15:08.015Z"},"bugs":{"url":"https://github.com/BluMintInc/use-context-selector/issues"},"author":{"name":"BluMintInc"},"license":"MIT","homepage":"https://github.com/BluMintInc/use-context-selector#readme","keywords":["react","context","hooks","useContextSelector","shallow-comparison","blumintinc","performance","optimization"],"repository":{"url":"git+https://github.com/BluMintInc/use-context-selector.git","type":"git"},"description":"React useContextSelector hook with shallow object comparison - BluMintInc fork","contributors":[{"url":"original author","name":"Daishi Kato"},{"name":"BluMintInc Team"}],"maintainers":[{"email":"oconnor.joseph@columbia.edu","name":"oconnorjoseph"},{"email":"javier@blumint.io","name":"javier.blumint"}],"readme":"# @blumintinc/use-context-selector\n\n[![npm](https://img.shields.io/npm/v/@blumintinc/use-context-selector)](https://www.npmjs.com/package/@blumintinc/use-context-selector)\n[![GitHub](https://img.shields.io/github/license/BluMintInc/use-context-selector)](https://github.com/BluMintInc/use-context-selector/blob/main/LICENSE)\n\nReact useContextSelector hook with shallow object comparison\n\n> **BluMintInc Fork**: This is an enhanced fork of the original [use-context-selector](https://github.com/dai-shi/use-context-selector) by Daishi Kato.\n> \n> **Key Enhancement**: Added shallow object comparison for selected values, preventing unnecessary re-renders when selectors return new objects with identical properties.\n\n## What's New in This Fork\n\n### Shallow Object Comparison\n\nThe original library uses strict reference equality (`Object.is`) to determine if selected values have changed. This fork enhances the comparison logic to perform **shallow object comparison** for plain objects:\n\n- **Plain objects**: Compares properties one level deep using `Object.is` for each property\n- **Primitives, arrays, and other values**: Uses `Object.is` (same as original)\n\n**Why this matters:**\n\n```javascript\n// With the original library, this would cause unnecessary re-renders:\nconst user = useContextSelector(context, (state) => ({\n  id: state.user.id,\n  name: state.user.name\n}));\n// Each render creates a new object reference, triggering re-renders even when id and name haven't changed\n\n// With this fork, the above pattern works efficiently!\n// Re-renders only happen when id or name actually changes\n```\n\nThis enhancement is particularly useful when:\n- Your selectors naturally return plain objects\n- You want cleaner selector code without manual memoization\n- You're extracting multiple related properties from context\n\n> [!IMPORTANT]\n> The goal of this library is to emulate the behavior of the React Context API with Concurrent React.\n> Many users try to use this library to avoid re-renders without needing to consider Concurrent React.\n> If you simply want to avoid re-renders, we recommend one of the following:\n> - [Zustand](https://github.com/pmndrs/zustand)\n> - [A naive implementation with useSyncExternalStore](https://github.com/dai-shi/use-context-selector/issues/109#issuecomment-1785147682)\n> - [Experimental react18-use](https://github.com/dai-shi/react18-use)\n> \n> [Learn more](https://github.com/dai-shi/use-context-selector/issues/149)\n\n## Introduction\n\nReact Context and useContext is often used to avoid prop drilling,\nhowever it's known that there's a performance issue.\nWhen a context value is changed, all components that useContext\nwill re-render.\n\nTo solve this issue,\n[useContextSelector](https://github.com/reactjs/rfcs/pull/119)\nis proposed and later proposed\n[Speculative Mode](https://github.com/reactjs/rfcs/pull/150)\nwith context selector support.\nThis library provides the API in userland.\n\nPrior to v1.3, it uses `changedBits=0` feature to stop propagation,\nv1.3 no longer depends on this undocumented feature.\n\n## Install\n\nThis package requires some peer dependencies, which you need to install by yourself.\n\n```bash\nnpm install @blumintinc/use-context-selector react scheduler\n```\n\nOr with yarn:\n\n```bash\nyarn add @blumintinc/use-context-selector react scheduler\n```\n\nOr with pnpm:\n\n```bash\npnpm add @blumintinc/use-context-selector react scheduler\n```\n\nNotes for library authors:\n\nPlease do not forget to keep `\"peerDependencies\"` and\nnote instructions to let users to install peer dependencies.\n\n## Technical memo\n\nTo make it work like original React context, it uses\n[useReducer cheat mode](https://overreacted.io/a-complete-guide-to-useeffect/#why-usereducer-is-the-cheat-mode-of-hooks) intentionally.\n\nIt also requires `useContextUpdate` to behave better in concurrent rendering.\nIts usage is optional and only required if the default behavior is unexpected.\n\n## Usage\n\n```javascript\nimport { useState } from 'react';\nimport { createRoot } from 'react-dom/client';\n\nimport { createContext, useContextSelector } from '@blumintinc/use-context-selector';\n\nconst context = createContext(null);\n\nconst Counter1 = () => {\n  const count1 = useContextSelector(context, (v) => v[0].count1);\n  const setState = useContextSelector(context, (v) => v[1]);\n  const increment = () =>\n    setState((s) => ({\n      ...s,\n      count1: s.count1 + 1,\n    }));\n  return (\n    <div>\n      <span>Count1: {count1}</span>\n      <button type=\"button\" onClick={increment}>\n        +1\n      </button>\n      {Math.random()}\n    </div>\n  );\n};\n\nconst Counter2 = () => {\n  const count2 = useContextSelector(context, (v) => v[0].count2);\n  const setState = useContextSelector(context, (v) => v[1]);\n  const increment = () =>\n    setState((s) => ({\n      ...s,\n      count2: s.count2 + 1,\n    }));\n  return (\n    <div>\n      <span>Count2: {count2}</span>\n      <button type=\"button\" onClick={increment}>\n        +1\n      </button>\n      {Math.random()}\n    </div>\n  );\n};\n\nconst StateProvider = ({ children }) => (\n  <context.Provider value={useState({ count1: 0, count2: 0 })}>\n    {children}\n  </context.Provider>\n);\n\nconst App = () => (\n  <StateProvider>\n    <Counter1 />\n    <Counter2 />\n  </StateProvider>\n);\n\ncreateRoot(document.getElementById('app')).render(<App />);\n```\n\n## API\n\n<!-- Generated by documentation.js. Update this documentation by updating the source code. -->\n\n### createContext\n\nThis creates a special context for `useContextSelector`.\n\n#### Parameters\n\n*   `defaultValue` **Value**&#x20;\n\n#### Examples\n\n```javascript\nimport { createContext } from '@blumintinc/use-context-selector';\n\nconst PersonContext = createContext({ firstName: '', familyName: '' });\n```\n\n### useContextSelector\n\nThis hook returns context selected value by selector.\n\nIt will only accept context created by `createContext`.\nIt will trigger re-render if only the selected value is referentially changed.\nFor object values, it performs shallow comparison (comparing each property with Object.is).\n\nThe selector should return referentially equal result for same input for better performance.\n\n#### Parameters\n\n*   `context` **Context\\<Value>**&#x20;\n*   `selector` **function (value: Value): Selected**&#x20;\n\n#### Examples\n\n```javascript\nimport { useContextSelector } from '@blumintinc/use-context-selector';\n\nconst firstName = useContextSelector(PersonContext, (state) => state.firstName);\n```\n\n### useContext\n\nThis hook returns the entire context value.\nUse this instead of React.useContext for consistent behavior.\n\n#### Parameters\n\n*   `context` **Context\\<Value>**&#x20;\n\n#### Examples\n\n```javascript\nimport { useContext } from '@blumintinc/use-context-selector';\n\nconst person = useContext(PersonContext);\n```\n\n### useContextUpdate\n\nThis hook returns an update function to wrap an updating function\n\nUse this for a function that will change a value in\nconcurrent rendering in React 18.\nOtherwise, there's no need to use this hook.\n\n#### Parameters\n\n*   `context` **Context\\<Value>**&#x20;\n\n#### Examples\n\n```javascript\nimport { useContextUpdate } from '@blumintinc/use-context-selector';\n\nconst update = useContextUpdate();\n\n// Wrap set state function\nupdate(() => setState(...));\n\n// Experimental suspense mode\nupdate(() => setState(...), { suspense: true });\n```\n\n### BridgeProvider\n\nThis is a Provider component for bridging multiple react roots\n\n#### Parameters\n\n*   `$0` **{context: Context\\<any>, value: any, children: ReactNode}**&#x20;\n\n    *   `$0.context` &#x20;\n    *   `$0.value` &#x20;\n    *   `$0.children` &#x20;\n\n#### Examples\n\n```javascript\nconst valueToBridge = useBridgeValue(PersonContext);\nreturn (\n  <Renderer>\n    <BridgeProvider context={PersonContext} value={valueToBridge}>\n      {children}\n    </BridgeProvider>\n  </Renderer>\n);\n```\n\n### useBridgeValue\n\nThis hook return a value for BridgeProvider\n\n#### Parameters\n\n*   `context` **Context\\<any>**&#x20;\n\n## Limitations\n\n*   In order to stop propagation, `children` of a context provider has to be either created outside of the provider or memoized with `React.memo`.\n*   Provider trigger re-renders only if the context value is referentially changed.\n*   Neither context consumers or class components are supported.\n*   The [stale props](https://react-redux.js.org/api/hooks#stale-props-and-zombie-children) issue exists in React 17 and below. (Can be resolved with `unstable_batchedUpdates`)\n*   Tearing is only avoided if all consumers get data using `useContextSelector`. If you use both props and `use-context-selector` to pass the same data, they may provide inconsistence data for a brief moment. (`02_tearing_spec` fails)\n\n## Examples\n\nThe [examples](examples) folder contains working examples.\nYou can run one of them with\n\n```bash\nPORT=8080 pnpm run examples:01_counter\n```\n\nand open <http://localhost:8080> in your web browser.\n\nYou can also try them directly:\n[01](https://stackblitz.com/github/dai-shi/use-context-selector/tree/main/examples/01_counter)\n[02](https://stackblitz.com/github/dai-shi/use-context-selector/tree/main/examples/02_person)\n[03](https://stackblitz.com/github/dai-shi/use-context-selector/tree/main/examples/03_suspense)\n\n## Projects that use use-context-selector\n\n*   [react-tracked](https://github.com/dai-shi/react-tracked)\n*   [use-atom](https://github.com/dai-shi/use-atom)\n*   [react-hooks-fetch](https://github.com/dai-shi/react-hooks-fetch)\n","readmeFilename":"README.md"}