Skip to content

Commit f1c299c

Browse files
committed
update README
1 parent 652fba2 commit f1c299c

File tree

3 files changed

+28
-17
lines changed

3 files changed

+28
-17
lines changed

README.md

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,19 @@
22

33
👻🔭 *Isolate Jotai atoms with scope*
44

5+
### Install
6+
7+
```
8+
npm install jotai-scope
9+
```
10+
511
## ScopeProvider
612

7-
`<ScopeProvider>` lets you reuse the *same* atoms in different parts of the React tree **without sharing state** while still being able to read other atoms from the parent store.
13+
While Jotai's Provider allows to scope Jotai's store under a subtree, we can't use the store above the tree within the subtree.
814

9-
---
15+
A workaround is to use `store` option in useAtom and other hooks.
16+
17+
Instead of specifying the `store` option, `<ScopeProvider>` lets you reuse the *same* atoms in different parts of the React tree **without sharing state** while still being able to read other atoms from the parent store.
1018

1119
### At‑a‑glance
1220

@@ -16,8 +24,6 @@
1624
* **Nested lookup.** If a scope can’t find the atom in the current scope, it inherits from the nearest parent scope, up to the nearest store.
1725
* Scoping works for both reading from atoms *and* writing to atoms.
1826

19-
---
20-
2127
### Quick Start
2228

2329
```tsx
@@ -56,8 +62,6 @@ export default function App() {
5662

5763
The second counter owns a private `doubledAtom` *and* a private `countAtom` because `doubledAtom` is scoped.
5864

59-
---
60-
6165
**2 · Nested scopes**
6266

6367
```tsx
@@ -72,8 +76,6 @@ The second counter owns a private `doubledAtom` *and* a private `countAtom` b
7276
* Outer scope (S1) isolates `countAtom`.
7377
* Inner scope (S2) isolates `nameAtom`, then looks up the tree and finds `countAtom` in S1.
7478

75-
---
76-
7779
**3 · Providing default values**
7880

7981
```tsx
@@ -84,8 +86,6 @@ The second counter owns a private `doubledAtom` *and* a private `countAtom` b
8486

8587
Mix tuples and plain atoms as needed: `atoms={[[countAtom, 1], anotherAtom]}`.
8688

87-
---
88-
8989
**4 · Scoping an atomFamily**
9090

9191
```tsx
@@ -103,8 +103,6 @@ const itemFamily = atomFamily((id: number) => atom(id))
103103

104104
Inside the `<ScopeProvider>` every `itemFamily(id)` call resolves to a scoped copy, so items rendered inside the provider are independent from the global ones and from any sibling scopes.
105105

106-
---
107-
108106
**A helpful syntax for describing nested scopes**
109107

110108
```
@@ -130,17 +128,24 @@ interface ScopeProviderProps {
130128
}
131129
```
132130

133-
---
134-
135131
### Caveats
136132

137133
* Avoid side effects inside atom read—it may run multiple times per scope. For async atoms, use an abort controller. The extra renders are a known limitation and solutions are being researched. If you are interested in helping, please [join the discussion](https://github.com/jotaijs/jotai-scope/issues/25).
138134

139-
---
135+
136+
<Stackblitz id="vitejs-vite-ctcuhj" file="src%2FApp.tsx" />
140137

141138
## createIsolation
142139

143-
`createIsolation` is useful for library authors to create a private store for libraries that use Jotai. It provides a private `Provider` and hooks that do not share state with the global store.
140+
Both Jotai's Provider and `jotai-scope`'s scoped provider
141+
are still using global contexts.
142+
143+
If you are developing a library that depends on Jotai and
144+
the library user may use Jotai separately in their apps,
145+
they can share the same context. This can be troublesome
146+
because they point to unexpected Jotai stores.
147+
148+
To avoid conflicting the contexts, a utility function called `createIsolation` is exported from `jotai-scope`.
144149

145150
```tsx
146151
import { createIsolation } from 'jotai-scope'

src/ScopeProvider/scope.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type {
77
Scope,
88
ScopedStore,
99
Store,
10+
WithOriginal,
1011
} from '../types'
1112
import { SCOPE } from '../types'
1213

@@ -184,10 +185,11 @@ export function createScope({
184185
*/
185186
function cloneAtom<T>(originalAtom: Atom<T>, implicitScope?: Scope) {
186187
// avoid reading `init` to preserve lazy initialization
187-
const scopedAtom: Atom<T> = Object.create(
188+
const scopedAtom: WithOriginal<Atom<T>> = Object.create(
188189
Object.getPrototypeOf(originalAtom),
189190
Object.getOwnPropertyDescriptors(originalAtom)
190191
)
192+
scopedAtom.originalAtom = originalAtom
191193

192194
if (scopedAtom.read !== defaultRead) {
193195
scopedAtom.read = createScopedRead<typeof scopedAtom>(

src/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,7 @@ export type Scope = {
5252
export const SCOPE = Symbol('scope')
5353

5454
export type AtomDefault = readonly [AnyWritableAtom, unknown]
55+
56+
export type WithOriginal<T extends AnyAtom> = T & {
57+
originalAtom: T
58+
}

0 commit comments

Comments
 (0)