Skip to content

Commit 820af7a

Browse files
authored
Merge pull request #17 from yf-yang/main
feat: add read and write and subscribe
2 parents c1878a3 + dd37b66 commit 820af7a

File tree

7 files changed

+1143
-138
lines changed

7 files changed

+1143
-138
lines changed

.changeset/thick-dolphins-worry.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
'jotai-x': major
3+
---
4+
5+
1. Rename `get` to `useValue`, `set` to `useSet`, `use` to `useState`.
6+
2. `use<Name>Store().store()` -> `use<Name>Store().store`.
7+
3. `use<Name>Store().get.value(option)` and `use<Name>Store().set.value(option)`'s `option` parameters are no longer supported. Pass the option to `use<Name>Store()` instead.
8+
4. Rename APIs:
9+
- `use<Name>Store().get.key()` -> `use<Name>Store().useKeyValue()`
10+
- `use<Name>Store().get.key()` -> `use<Name>Store().useValue('key')`
11+
- `use<Name>Store().set.key()` -> `use<Name>Store().useSetKey()`
12+
- `use<Name>Store().set.key()` -> `use<Name>Store().useSet('key')`
13+
- `use<Name>Store().use.key()` -> `use<Name>Store().useKeyState()`
14+
- `use<Name>Store().use.key()` -> `use<Name>Store().useState('key')`
15+
- `use<Name>Store().get.atom(atomConfig)` -> `use<Name>Store().useAtomValue(atomConfig)`
16+
- `use<Name>Store().set.atom(atomConfig)` -> `use<Name>Store().useSetAtom(atomConfig)`
17+
- `use<Name>Store().use.atom(atomConfig)` -> `use<Name>Store().useAtomState(atomConfig)`
18+
5. More APIs to directly get/set/subscribe atom states:
19+
- `use<Name>Store().getKey()`
20+
- `use<Name>Store().get('key')`
21+
- `use<Name>Store().setKey(...args)`
22+
- `use<Name>Store().set('key', ...args)`
23+
- `use<Name>Store().subscribeKey(...args)`
24+
- `use<Name>Store().subscribe('key', ...args)`
25+
- `use<Name>Store().getAtom(atomConfig)`
26+
- `use<Name>Store().setAtom(atomConfig, ...args)`
27+
- `use<Name>Store().subscribeAtom(atomConfig, ...args)`
28+

README.md

Lines changed: 129 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# JotaiX
22

3+
[Migrating from v1 to v2](#migrate-from-v1-to-v2)
4+
35
JotaiX is a custom extension of [Jotai](https://github.com/pmndrs/jotai), a primitive and flexible state management library for React. Jotai offers a
46
minimalistic API to manage global, derived, or async states in React, solving common issues such as unnecessary
57
re-renders or complex context management. JotaiX builds upon this foundation, providing enhanced utilities and patterns
@@ -63,12 +65,55 @@ The **`options`** object can include several properties to customize the behavio
6365
The **`createAtomStore`** function returns an object (**`AtomStoreApi`**) containing the following properties and methods for interacting with the store:
6466
6567
- **`use<Name>Store`**:
66-
- A function that returns the following objects: **`get`**, **`set`**, **`use`** and **`store`**, where values are hooks for each state defined in the store.
67-
- **`get`**: Hooks for accessing a state within a component, ensuring re-rendering when the state changes. See [useAtomValue](https://jotai.org/docs/core/use-atom#useatomvalue).
68-
- **`set`**: Hooks for setting a state within a component. See [useSetAtom](https://jotai.org/docs/core/use-atom#usesetatom).
69-
- **`use`**: Hooks for accessing and setting a state within a component, ensuring re-rendering when the state changes. See [useAtom](https://jotai.org/docs/core/use-atom).
70-
- **`store`**: A hook to access the [JotaiStore](https://jotai.org/docs/core/store) for the current context.
71-
- Example: `const [element, setElement] = useElementStore().use.element()`
68+
- A function that returns the following objects: **`useValue`**, **`useSet`**, **`useState`**, where values are hooks for each state defined in the store, and **`get`**, **`set`**, **`subscribe`**, **`store`**, where values are direct get/set accessors to modify each state.
69+
- **`useValue`**: Hooks for accessing a state within a component, ensuring re-rendering when the state changes. See [useAtomValue](https://jotai.org/docs/core/use-atom#useatomvalue).
70+
``` js
71+
const store = useElementStore();
72+
const element = store.useElementValue();
73+
// alternative
74+
const element = useElementStore().useValue('element');
75+
```
76+
- **`useSet`**: Hooks for setting a state within a component. See [useSetAtom](https://jotai.org/docs/core/use-atom#usesetatom).
77+
``` js
78+
const store = useElementStore();
79+
const element = store.useSetElement();
80+
// alternative
81+
const element = useElementStore().useSet('element');
82+
```
83+
- **`useState`**: Hooks for accessing and setting a state within a component, ensuring re-rendering when the state changes. See [useAtom](https://jotai.org/docs/core/use-atom).
84+
``` js
85+
const store = useElementStore();
86+
const element = store.useElementState();
87+
// alternative
88+
const element = useElementStore().useState('element');
89+
```
90+
- **`get`**: Directly get the state. Not a hook so it could be used in event handlers or other hooks, and the component won't re-render if the state changes. See [createStore](https://jotai.org/docs/core/store#createstore)
91+
``` js
92+
const store = useElementStore();
93+
useEffect(() => { console.log(store.getElement()) }, []);
94+
// alternative
95+
useEffect(() => { console.log(store.get('element')) }, []);
96+
```
97+
- **`set`**: Directly set the state. Not a hook so it could be used in event handlers or other hooks. See [createStore](https://jotai.org/docs/core/store#createstore)
98+
``` js
99+
const store = useElementStore();
100+
useEffect(() => { store.setElement('div') }, []);
101+
// alternative
102+
useEffect(() => { store.set('element', 'div') }, []);
103+
```
104+
- **`subscribe`**: Subscribe to the state change. . See [createStore](https://jotai.org/docs/core/store#createstore)
105+
- NOTE: The subscribed callback will fire whenever the atom state or dependent atom states change. There is no equality check.
106+
``` js
107+
const store = useElementStore();
108+
useEffect(() => store.subscribeElement((newElement) => console.log(newElement)), []);
109+
// alternative
110+
useEffect(() => store.subscribe('element', (newElement) => console.log(newElement)), []);
111+
```
112+
- **`store`**: The [JotaiStore](https://jotai.org/docs/core/store) for the current context.
113+
``` js
114+
const store = useElementStore();
115+
const jotaiStore = store.store;
116+
```
72117
- **`<Name>Provider`**:
73118
- The API includes dynamically generated provider components for each defined store. This allows scoped state management within your application. More information in the next section.
74119
- **`<name>Store`**:
@@ -103,20 +148,24 @@ const { useUserStore } = createAtomStore({
103148
}),
104149
});
105150
106-
const intro = useAppStore().get.intro();
151+
const userStore = useUserStore();
152+
const intro = userStore.useIntroValue();
107153
```
108154

109155
#### Externally Defined Derived Atoms
110156

111-
Derived atoms can also be defined externally by accessing the store's atoms through the `<name>Store` API. Externally defined atoms can be accessed through the store using the special `use<Name>Store().{get,set,use}.atom` hooks.
157+
Derived atoms can also be defined externally by accessing the store's atoms through the `<name>Store` API. Externally defined atoms can be accessed through the store using special hooks:
158+
`useAtomValue`, `useSetAtom`, `useAtomState`, `getAtom`, `setAtom`, `subscribeAtom`.
112159

113160
```ts
114161
const { userStore, useUserStore } = createAtomStore({
115162
username: 'Alice',
116163
}, { name: 'user' });
117164
118165
const introAtom = atom((get) => `My name is ${get(userStore.atom.username)}`);
119-
const intro = useUserStore().get.atom(introAtom);
166+
167+
const userStore = useUserStore();
168+
const intro = userStore.useAtomValue(introAtom);
120169
```
121170

122171
### Example Usage
@@ -162,8 +211,20 @@ const App = () => {
162211
};
163212
164213
const Component = () => {
165-
const [name, setName] = useAppStore().use.name();
166-
const onUpdateName = useAppStore().get.onUpdateName();
214+
const appStore = useAppStore();
215+
const [name, setName] = store.useNameState();
216+
const onUpdateName = store.useOnUpdateNameValue();
217+
218+
useEffect(() => store.subscribe.name((newName) => {
219+
console.log(`Name updated to: ${newName}`);
220+
// An alternative to `appStore.useNameState()`, won't rerender when the state changes
221+
assert.ok(newName === appStore.getName());
222+
if (newName.includes('#')) {
223+
// Equivalent to `appStore.useSetName()`'s return
224+
appStore.setName('invalid');
225+
onUpdateName('invalid');
226+
}
227+
}), [appStore])
167228
168229
return (
169230
<div>
@@ -211,9 +272,11 @@ const App = () => {
211272
// Accessing state from the specified scope.
212273
const Component = () => {
213274
// Here, we get the state from the parent scope
214-
const [name, setName] = useAppStore('parent').use.name();
275+
const parentAppStore = useAppStore('parent');
276+
const [name, setName] = parentScope.useNameState();
215277
// Here, we get the state from the closest scope (default)
216-
const onUpdateName = useAppStore().get.onUpdateName();
278+
const appStore = useAppStore();
279+
const onUpdateName = appStore.useOnUpdateNameValue();
217280
218281
return (
219282
<div>
@@ -224,6 +287,59 @@ const Component = () => {
224287
};
225288
```
226289

290+
## Migrate from v1 to v2
291+
292+
1. Return of `use<Name>Store`: `get` is renamed to `use<Key>Value`, `set` is renamed to `useSet<Key>`, `use` is renamed to `useState`.
293+
``` diff
294+
- const name = useAppStore().get.name();
295+
- const setName = useAppStore().set.name();
296+
- const [name, setName] = useAppStore().use.name();
297+
298+
+ const appStore = useAppStore();
299+
+ const name = appStore.useNameValue();
300+
+ const setName = appStore.useSetName();
301+
+ const [name, setName] = appStore.useNameState();
302+
303+
+ // alternative
304+
+ const name = appStore.useValue('name');
305+
+ const setName = appStore.useSet('name');
306+
+ const [name, setName] = appStore.useState('name');
307+
```
308+
309+
2. Rename `.atom()` APIs:
310+
``` diff
311+
- const atomValue = useAppStore().get.atom(atomConfig);
312+
- const setAtomValue = useAppStore().set.atom(atomConfig);
313+
- const [atomValue, setAtomValue] = useAppStore().use.atom(atomConfig);
314+
315+
+ const appStore = useAppStore();
316+
+ const atomValue = appStore.useAtomValue(atomConfig);
317+
+ const setAtomValue = appStore.useSetAtom(atomConfig);
318+
+ const [atomValue, setAtomValue] = appStore.useAtomState(atomConfig);
319+
```
320+
NOTE: Try to avoid using the key "atom" as the store state key because
321+
1. `useValue('atom')` and `useSet('atom')` and `useState('atom')` are not supported. They are only valid if the key "atom" is presented in the store.
322+
2. On the other hand, `useAtomValue()`, `useSetAtom()`, and `useAtomState()` cannot access the state if the key "atom" is presented in the store.
323+
324+
3. Return of `use<Name>Store`: `store` is no longer a function. Now it is a direct property.
325+
``` diff
326+
- const store = useAppStore().store();
327+
328+
+ const appStore = useAppStore();
329+
+ const jotaiStore = appStore.store;
330+
```
331+
332+
4. Return of `use<Name>Store`: `option` is no longer a valid parameter of `useValue` and `useSet`. To control the behavior, directly pass the options to `createAtomStore` or `use<Name>Store`.
333+
``` diff
334+
- const scope1Name = useAppStore().useValue.name(scope1Options);
335+
- const scope2Name = useAppStore().useValue.name(scope2Options);
336+
337+
+ const scope1AppStore = useAppStore(scope1Options);
338+
+ const scope1Name = scope1AppStore.useNameValue();
339+
+ const scope2AppStore = useAppStore(scope2Options);
340+
+ const scope2Name = scope2AppStore.useNameValue();
341+
```
342+
227343
## Contributing
228344
229345
### Ideas and discussions

0 commit comments

Comments
 (0)