{"_id":"@aminer/jotai-recoil-adapter","name":"@aminer/jotai-recoil-adapter","dist-tags":{"latest":"0.0.2"},"versions":{"0.0.2":{"name":"@aminer/jotai-recoil-adapter","version":"0.0.2","description":"Drop-in adapter for migrating to Jotai from Recoil","license":"MIT","keywords":["jotai","recoil","plugin","react","state","manager","management","store"],"main":"./lib/module/index.js","types":"./lib/typescript/src/index.d.ts","exports":{".":{"source":"./src/index.tsx","types":"./lib/typescript/src/index.d.ts","default":"./lib/module/index.js"},"./package.json":"./package.json"},"scripts":{"test":"jest","typecheck":"tsc","lint":"eslint \"**/*.{js,ts,tsx}\"","clean":"del-cli lib","prepare":"bob build","release":"release-it --only-version"},"repository":{"type":"git","url":"git+https://github.com/aminerol/jotai-recoil-adapter.git"},"author":{"name":"aminerol","email":"zazamine8@gmail.com","url":"https://github.com/aminerol"},"bugs":{"url":"https://github.com/aminerol/jotai-recoil-adapter/issues"},"homepage":"https://github.com/aminerol/jotai-recoil-adapter#readme","publishConfig":{"registry":"https://registry.npmjs.org/","access":"public"},"dependencies":{"fast-deep-equal":"^3.1.3","jotai":"^2.6.1"},"devDependencies":{"@commitlint/config-conventional":"^19.6.0","@eslint/compat":"^1.2.7","@eslint/eslintrc":"^3.3.0","@eslint/js":"^9.22.0","@evilmartians/lefthook":"^1.5.0","@release-it/conventional-changelog":"^9.0.2","@types/jest":"^29.5.5","@types/react":"^19.0.12","commitlint":"^19.6.1","del-cli":"^5.1.0","eslint":"^9.22.0","eslint-config-prettier":"^10.1.1","eslint-plugin-prettier":"^5.2.3","jest":"^29.7.0","prettier":"^3.0.3","react":"19.0.0","react-native-builder-bob":"^0.40.13","release-it":"^17.10.0","typescript":"^5.8.3"},"peerDependencies":{"react":"*"},"commitlint":{"extends":["@commitlint/config-conventional"]},"release-it":{"git":{"commitMessage":"chore: release ${version}","tagName":"v${version}"},"npm":{"publish":true},"github":{"release":true},"plugins":{"@release-it/conventional-changelog":{"preset":{"name":"angular"}}}},"prettier":{"quoteProps":"consistent","singleQuote":true,"tabWidth":2,"trailingComma":"es5","useTabs":false},"react-native-builder-bob":{"source":"src","output":"lib","targets":[["module",{"esm":true}],["typescript",{"project":"tsconfig.build.json"}]]},"create-react-native-library":{"languages":"js","type":"library","version":"0.52.0"},"_id":"@aminer/jotai-recoil-adapter@0.0.2","gitHead":"a592506ad89af91292725f60313c8f622b9506de","_nodeVersion":"22.17.1","_npmVersion":"10.9.2","dist":{"integrity":"sha512-LeaQSgOztWDwDfof90IEfs8xNAfjEUfjdmS6xU8+xVvQEoGES7FS0vPa9VmJtAzJ8tTqXM3Zrm9vbIIccjMEng==","shasum":"59a364ae94fb26ebdab695a839e764146d7c556c","tarball":"https://registry.npmjs.org/@aminer/jotai-recoil-adapter/-/jotai-recoil-adapter-0.0.2.tgz","fileCount":75,"unpackedSize":94049,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQD2o5PSqn4aCvqO/Gfxit5k3s6qmmcgOLpAEfxnPMBeMQIgdBC0XVuAl675389inP/dkpBKiMARNUE87lfvfUt8yT8="}]},"_npmUser":{"name":"aminer","email":"zazamine8@gmail.com"},"directories":{},"maintainers":[{"name":"aminer","email":"zazamine8@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/jotai-recoil-adapter_0.0.2_1754145251731_0.7774826280246123"},"_hasShrinkwrap":false}},"time":{"created":"2025-08-02T14:34:11.667Z","0.0.2":"2025-08-02T14:34:11.985Z","modified":"2025-08-02T14:34:12.195Z"},"maintainers":[{"name":"aminer","email":"zazamine8@gmail.com"}],"description":"Drop-in adapter for migrating to Jotai from Recoil","homepage":"https://github.com/aminerol/jotai-recoil-adapter#readme","keywords":["jotai","recoil","plugin","react","state","manager","management","store"],"repository":{"type":"git","url":"git+https://github.com/aminerol/jotai-recoil-adapter.git"},"author":{"name":"aminerol","email":"zazamine8@gmail.com","url":"https://github.com/aminerol"},"bugs":{"url":"https://github.com/aminerol/jotai-recoil-adapter/issues"},"license":"MIT","readme":"# jotai-recoil-adapter\n\n`jotai-recoil-adapter` is a library designed to facilitate the transition from Recoil to Jotai in React applications. It provides a drop-in Recoil-compatible API built on top of Jotai's state management capabilities.\n\n<!-- TOC -->\n\n- [jotai-recoil-adapter](#jotai-recoil-adapter)\n  - [Quick Start](#quick-start)\n  - [Usage](#usage)\n    - [`RecoilRoot`](#recoilroot)\n    - [`atom`](#atom)\n    - [`atomAsync`](#atomasync)\n    - [`selector`](#selector)\n      - [Composing Jotai Atoms with `jotai-recoil-adapter`](#composing-jotai-atoms-with-jotai-recoil-adapter)\n    - [`selectorDefault`](#selectordefault)\n    - [`selector` (asynchronous) as `asyncSelector`](#selector-asynchronous-as-asyncselector)\n    - [`selectorFamily`](#selectorfamily)\n    - [`selectorFamilyDefault`](#selectorfamilydefault)\n    - [`atomFamily`](#atomfamily)\n    - [`atomFamilyAsync`](#atomfamilyasync)\n    - [`useRecoilState`](#userecoilstate)\n    - [`useRecoilValue`](#userecoilvalue)\n    - [`useSetRecoilState`](#usesetrecoilstate)\n    - [`useRecoilCallback`](#userecoilcallback)\n    - [`useRecoilBridgeAcrossReactRootsUNSTABLE` (use with React portal)](#userecoilbridgeacrossreactrootsunstable-use-with-react-portal)\n    - [`useRecoilBridgeAcrossReactRootsUNSTABLE` (use with react-three-fiber)](#userecoilbridgeacrossreactrootsunstable-use-with-react-three-fiber)\n  - [Use-Case](#use-case)\n  - [Warnings and Guidance](#warnings-and-guidance)\n    - [Performance Concerns](#performance-concerns)\n    - [Other Potential Concerns](#other-potential-concerns)\n    - [Incompleteness of Recoil API in `jotai-recoil-adapter`](#incompleteness-of-recoil-api-in-jotai-recoil-adapter)\n  - [Contributing](#contributing)\n  - [Support and Issues](#support-and-issues)\n  - [License](#license)\n\n<!-- /TOC -->\n\n## Quick Start\n\n```\nnpm i jotai-recoil-adapter\n```\n\nThe adapter supports the following Recoil features:\n\n```ts\nimport {\n  // standard Recoil APIs\n  RecoilRoot,\n  atom,\n  atomFamily,\n  selector,\n  selectorFamily,\n  useRecoilCallback,\n  useRecoilState,\n  useRecoilValue,\n  useSetRecoilState,\n  useResetRecoilState,\n  useRecoilBridgeAcrossReactRoots_UNSTABLE,\n  // special adapters for compatibility\n  atomAsync,\n  atomFamilyAsync,\n  asyncSelector,\n  asyncSelectorFamily,\n  selectorDefault,\n  // non-standard adaptations, see Readme\n  waitForAll,\n} from 'jotai-recoil-adapter';\n```\n\n_Please read the [Use Case](#use-case) and [Warnings and Guidance](#warnings-and-guidance) section before use in business applications._\n\n## Usage\n\n### `RecoilRoot`\n\nConvenience adapter for Recoil's `<RecoilRoot />` component.\n\nThis component does nothing. It merely returns a `<React.Fragment />` that wraps child components.\n\n```tsx\nimport { RecoilRoot } from 'jotai-recoil-adapter';\n\nconst App = () => <RecoilRoot> /* ... */ </RecoilRoot>;\n```\n\n### `atom`\n\nBasic atom creation:\n\n```jsx\nimport { atom } from 'jotai-recoil-adapter';\n\nconst textState = atom({\n  key: 'textState',\n  default: 'Hello',\n  effects: 'UNSUPPORTED',\n});\n```\n\n### `atomAsync`\n\nAdapter for Recoil `atom` that are initialized with an async selector. To be used with the `selectorDefault` adapter as a default value.\n\n```ts\ninterface AtomAsyncAdapterParams<T, U> {\n  ...,\n  default: Promise<T>;\n  effects?: \"UNSUPPORTED\";\n  fallback?: U;\n};\n```\n\n```jsx\nimport { atom, atomAsync, selectorDefault } from 'jotai-recoil-adapter';\n\nconst userIdState = atom({\n  key: 'userId',\n  default: 1,\n});\n\nconst userAtom = atomAsync({\n  key: 'userAtom',\n  default: selectorDefault({\n    key: 'userAtomDefaultValueSelector',\n    get: async ({ get }) => {\n      const userId = get(userIdState);\n      const response = await fetch(`/api/user/${userId}`);\n      return response.json();\n    },\n  }),\n  fallback: /* ... */\n});\n```\n\n_Why is the API different from Recoil? Jotai's API isn't perfectly compatible with Recoil's. Remember: this adapter exists to help ease the migration from Recoil to Jotai._\n\n**Important:** This adapter is implemented using Jotai's `getDefaultStore` method, therefore this is incompatible with custom Jotai store providers and must only be used in Jotai providerless mode.\n\n### `selector`\n\nBasic selector usage:\n\n```jsx\nimport { atom, selector } from 'jotai-recoil-adapter';\n\nconst countState = atom({\n  key: 'count-state',\n  default: Math.PI,\n});\n\nconst doubleCountSelector = selector({\n  key: 'double-count',\n  get: ({ get }) => get(countState) * 2,\n});\n```\n\n#### Composing Jotai Atoms with `jotai-recoil-adapter`\n\n`jotai-recoil-adapter` allows seamless composition of Jotai `atom` and `atomFamily` with its `selector`, `selectorFamily`, and `useRecoilCallback`. This enables complex state management patterns while maintaining a consistent API.\n\nIn this example, a Jotai `PrimitiveAtom` is used with a `selector` from `jotai-recoil-adapter`. This pattern is extendable to other combinations, allowing you to take full advantage of Jotai's capabilities within a Recoil-like API.\n\n```jsx\nimport { atom as jotaiAtom } from 'jotai';\nimport { selector, useRecoilValue } from 'jotai-recoil-adapter';\n\nconst countState = jotaiAtom(0);\n\nconst doubleCountSelector = selector({\n  key: '...',\n  get: ({ get }) => get(countState) * 2,\n});\n```\n\n### `selectorDefault`\n\nSpecial adapter for both synchronous and asynchronous Recoil `selector` that are used to initialize recoil `atom`.\n\n```jsx\nimport {\n  atom,\n  atomFamily,\n  selectorDefault\n} from 'jotai-recoil-adapter';\n\nconst idState = atom({\n  key: '...',\n  default: 1,\n});\n\nconst itemAtom = atom({\n  key: '...',\n  default: selectorDefault({\n    key: '...',\n    get: ({ get }) => {\n      const id = get(idState);\n      return createItem(id);\n    },\n  }),\n});\n\nconst itemStateFamily = atomFamily({\n  key: '...',\n  default: (itemId: string) => `Item ${itemId}`,\n});\n\nconst itemStateFamily = atomFamily({\n  key: '...',\n  default: selectorDefault({\n    key: '...',\n    get: ({ get }) => {\n      ...\n    }\n  }),\n});\n```\n\n**Important:** This adapter is implemented using Jotai's `getDefaultStore` method, therefore this is incompatible with custom Jotai store providers and must only be used in Jotai providerless mode.\n\n### `selector` (asynchronous) as `asyncSelector`\n\nAsynchronous selector implemented using a special adapter:\n\n```ts\ntype AsyncRecoilSelectorOptions<T, U> = {\n  key: string;\n  get: ({ get }) => Promise<T>;\n  fallback: U;\n};\n```\n\n```jsx\nimport { asyncSelector } from 'jotai-recoil-adapter';\n\nconst randomNumberSelector = asyncSelector<number, \"Crunching numbers...\">({\n  key: 'randomNumberSelector',\n  get: async ({ get }) => {\n    await new Promise(resolve => setTimeout(resolve, 1000));\n    return Math.random();\n  },\n  fallback: \"Crunching numbers...\"\n});\n\nconst userDataSelector = asyncSelector<User, null>({\n  key: 'userData',\n  get: async ({ get }) => {\n    const response = await fetch('/api/user/data');\n    return response.json();\n  },\n  fallback: null\n});\n```\n\n### `selectorFamily`\n\nAtom family for creating a series of atoms based on parameters:\n\n```jsx\nimport { selectorFamily } from 'jotai-recoil-adapter';\n\nconst itemStateFamily = selectorFamily({\n  key: '...',\n  default: (itemId: string) => ({ get }) => {\n    /* ... */\n  },\n});\n```\n\n### `selectorFamilyDefault`\n\nSpecial adapter for both synchronous and asynchronous Recoil `selectorFamily` that are used to initialize recoil `atomFamily`.\n\n```jsx\n\nimport {\n  atomFamily,\n  selectorDefaultFamily\n} from 'jotai-recoil-adapter';\n\nconst itemStateFamily = atomFamily({\n  key: '...',\n  default: selectorDefaultFamily({\n    key: '...',\n    get: (itemId: string) => ({ get }) => {\n      ...\n    }\n  }),\n});\n```\n\n**Important:** This adapter is implemented using Jotai's `getDefaultStore` method, therefore this is incompatible with custom Jotai store providers and must only be used in Jotai providerless mode.\n\n### `atomFamily`\n\nAtom family for creating a series of atoms based on parameters:\n\n```jsx\nimport {\n  atomFamily,\n  selectorDefault,\n  selectorDefaultFamily\n} from 'jotai-recoil-adapter';\n\nconst itemStateFamily = atomFamily({\n  key: `item-state-family`,\n  default: (itemId: string) => `Item ${itemId}`,\n});\n\nconst itemStateFamily = atomFamily({\n  key: '...',\n  default: selectorDefault({\n    key: '...',\n    get: ({ get }) => {\n      ...\n    }\n  }),\n});\n\nconst itemStateFamily = atomFamily({\n  key: '...',\n  default: selectorDefaultFamily({\n    key: '...',\n    get: (itemId: string) => ({ get }) => {\n      ...\n    }\n  }),\n});\n```\n\n### `atomFamilyAsync`\n\nAdapter for Recoil `atomFamily` that are initialized with an async selector. To be used with the `selectorDefaultFamily` and `selectorDefault` adapters as a default values:\n\n```ts\ninterface AtomFamilyAsyncAdapterParams<T, Param, U> {\n  default: Promise<T>;\n  effects?: 'UNSUPPORTED';\n  fallback?: U;\n}\n```\n\n```ts\nimport { selectorDefault, selectorDefaultFamily, atomAsync } from 'jotai-recoil-adapter';\n\nconst fooStateFamily = atomFamilyAsync({\n  key: 'foo-atom-family',\n  default: selectorDefaultFamily({\n     key: 'foo-atom-family-default-value-selector',\n     get: (param) => async ({ get }) => {\n      const data = await fetchData(`v1/api/data/${ param }`);\n      const composedValue = get(someOtherAtom);\n      return doStuffWith(composedValue, data);\n     }\n  }),\n  fallback: /* ... */\n});\n\nconst barStateFamily = atomFamilyAsync({\n  key: 'bar-atom-family',\n  default: selectorDefault({\n     key: 'bar-atom-family-default-value-selector',\n     get: async ({ get }) => {\n      const data = await fetchData();\n      const composedValue = get(someOtherAtom);\n      return doStuffWith(composedValue, data);\n     }\n  }),\n  fallback: /* ... */\n});\n```\n\n**Important:** This adapter is implemented using Jotai's `getDefaultStore` method, therefore this is incompatible with custom Jotai store providers and must only be used in Jotai providerless mode.\n\n### `useRecoilState`\n\nHook to read and write atom state:\n\n```jsx\nimport { useRecoilState } from 'jotai-recoil-adapter';\n\nconst [text, setText] = useRecoilState(textState);\n```\n\n### `useRecoilValue`\n\nHook to read atom or selector state:\n\n```jsx\nimport { useRecoilValue } from 'jotai-recoil-adapter';\n\nconst count = useRecoilValue(countSelector);\n```\n\n### `useSetRecoilState`\n\nHook to update atom state:\n\n```jsx\nimport { useSetRecoilState } from 'jotai-recoil-adapter';\n\nconst setCount = useSetRecoilState(countState);\n```\n\n### `useRecoilCallback`\n\nHook to interact with multiple atoms/selectors:\n\n```jsx\nimport { useRecoilCallback } from 'jotai-recoil-adapter';\n\nconst logCount = useRecoilCallback(\n  ({ snapshot }) => async () => {\n    const count = await snapshot.getPromise(countState);\n    console.log(count);\n  },\n  [...]\n);\n```\n\n### `useRecoilBridgeAcrossReactRoots_UNSTABLE` (use with React portal)\n\nUse Jotai's `Provider` component.\n\n### `useRecoilBridgeAcrossReactRoots_UNSTABLE` (use with react-three-fiber)\n\nRecommendation is to use Jotai's \"providerless mode\".\n\nsee: https://github.com/pmndrs/jotai/issues/683#issuecomment-995764515\n\n## Use-Case\n\nThe primary use-case for `jotai-recoil-adapter` is to facilitate an incremental migration from Recoil to Jotai. This is particularly useful in scenarios where:\n\n- **Large Codebases**: Applications with a significant investment in Recoil can migrate to Jotai gradually, without needing a complete rewrite.\n- **Testing and Stability**: It allows for piece-by-piece migration and testing, ensuring that the application remains stable and reliable throughout the process.\n- **Learning Curve**: Teams can adapt to Jotai's concepts and APIs at a comfortable pace, reducing the learning curve.\n- **Coexistence**: Enables the coexistence of Recoil and Jotai during the transition period, allowing for a phased-out deprecation of Recoil.\n\nThis approach minimizes disruption in development workflows and provides a path to leverage Jotai's simplicity and performance benefits without the upfront cost of a full-scale migration.\n\n## Warnings and Guidance\n\n### Performance Concerns\n\nWhile `jotai-recoil-adapter` aims to provide a seamless transition from Recoil to Jotai, there are potential performance implications to consider:\n\n- **Overhead**: The adapter introduces an additional abstraction layer, which could lead to slight performance overhead compared to using Jotai or Recoil directly.\n- **Optimization Differences**: Jotai and Recoil have different optimization strategies. Be aware that performance characteristics may change when migrating from Recoil to Jotai.\n\nYou should thoroughly test and measure your application before and after applying this adapter to insure against regressions.\n\n### Other Potential Concerns\n\n- **Context Propagation**: The context propagation mechanisms in Jotai and Recoil are different. This could lead to unexpected behavior, especially in complex component trees or when using context-dependent features.\n- **Error Handling**: The error handling paradigms in Jotai and Recoil might differ. Pay attention to how errors are handled and propagated in your application after the migration.\n\n### Incompleteness of Recoil API in `jotai-recoil-adapter`\n\n`jotai-recoil-adapter` does not cover the entire API surface of Recoil. Some advanced features and utilities provided by Recoil might not have equivalents in this adapter.\n\nIt's important to review your current usage of Recoil and determine if any advanced features critical to your application are not supported by the adapter. Plan for alternative solutions or adjustments in such cases.\n\n## Contributing\n\nContributions to `jotai-recoil-adapter` are welcome. To contribute, fork the repository, create a feature branch, commit your changes, and open a Pull Request.\n\nSee [CONTRIBUTING.md](./CONTRIBUTING.md)\n\n## Support and Issues\n\nFor issues or support questions, please file an issue on the GitHub repository.\n\n## License\n\n`jotai-recoil-adapter` is released under the MIT License.\n","readmeFilename":"README.md","_rev":"1-1f79f2682f51dad86fc46dada774a31d"}