Skip to content

Commit 4e3aa6b

Browse files
committed
fix: add tests
1 parent baa1ebc commit 4e3aa6b

File tree

5 files changed

+64
-22
lines changed

5 files changed

+64
-22
lines changed

.changeset/bright-coats-peel.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'jotai-x': patch
3+
---
4+
5+
Add test cases and fix bugs

README.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,7 @@ const [stars, setStars] = useAppState('stars');
153153
// With selector and deps
154154
const upperName = useAppValue('name', {
155155
selector: (name) => name.toUpperCase(),
156-
deps: []
157-
});
156+
}, []);
158157
```
159158

160159
#### 2. Store Instance Methods
@@ -204,14 +203,14 @@ const name = useAppValue('name');
204203
// With selector
205204
const upperName = useAppValue('name', {
206205
selector: (name) => name.toUpperCase(),
207-
deps: [] // Optional deps array
208-
});
206+
}, [] // if selector is not memoized, provide deps array
207+
);
209208

210209
// With equality function
211210
const name = useAppValue('name', {
212211
selector: (name) => name,
213212
equalityFn: (prev, next) => prev.length === next.length
214-
});
213+
}, []);
215214
```
216215

217216
#### `use<Name>Set(key)`
@@ -404,7 +403,7 @@ const selector = useCallback((name) => name.toUpperCase(), []);
404403
useUserValue('name', { selector });
405404

406405
// ✅ Correct - provide deps array
407-
useUserValue('name', { selector: (name) => name.toUpperCase(), deps: [] });
406+
useUserValue('name', { selector: (name) => name.toUpperCase() }, []);
408407

409408
// ✅ Correct - no selector
410409
useUserValue('name');

packages/jotai-x/README.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,7 @@ const [stars, setStars] = useAppState('stars');
153153
// With selector and deps
154154
const upperName = useAppValue('name', {
155155
selector: (name) => name.toUpperCase(),
156-
deps: []
157-
});
156+
}, []);
158157
```
159158

160159
#### 2. Store Instance Methods
@@ -204,14 +203,14 @@ const name = useAppValue('name');
204203
// With selector
205204
const upperName = useAppValue('name', {
206205
selector: (name) => name.toUpperCase(),
207-
deps: [] // Optional deps array
208-
});
206+
}, [] // if selector is not memoized, provide deps array
207+
);
209208

210209
// With equality function
211210
const name = useAppValue('name', {
212211
selector: (name) => name,
213212
equalityFn: (prev, next) => prev.length === next.length
214-
});
213+
}, []);
215214
```
216215

217216
#### `use<Name>Set(key)`
@@ -404,7 +403,7 @@ const selector = useCallback((name) => name.toUpperCase(), []);
404403
useUserValue('name', { selector });
405404

406405
// ✅ Correct - provide deps array
407-
useUserValue('name', { selector: (name) => name.toUpperCase(), deps: [] });
406+
useUserValue('name', { selector: (name) => name.toUpperCase() }, []);
408407

409408
// ✅ Correct - no selector
410409
useUserValue('name');

packages/jotai-x/src/createAtomStore.spec.tsx

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,8 @@ describe('createAtomStore', () => {
2929
arr: INITIAL_ARR,
3030
};
3131

32-
const { useMyTestStoreStore, MyTestStoreProvider } = createAtomStore(
33-
initialTestStoreValue,
34-
{ name: 'myTestStore' as const }
35-
);
32+
const { useMyTestStoreStore, MyTestStoreProvider, useMyTestStoreValue } =
33+
createAtomStore(initialTestStoreValue, { name: 'myTestStore' as const });
3634

3735
let numRenderCount = 0;
3836
const NumRenderer = () => {
@@ -92,6 +90,24 @@ describe('createAtomStore', () => {
9290
);
9391
};
9492

93+
let arrNumRenderCountWithOneHook = 0;
94+
const ArrNumRendererWithOneHook = () => {
95+
arrNumRenderCountWithOneHook += 1;
96+
const num = useMyTestStoreValue('num');
97+
const arrNum = useMyTestStoreValue(
98+
'arr',
99+
{
100+
selector: (v) => v[num],
101+
},
102+
[num]
103+
);
104+
return (
105+
<div>
106+
<div>arrNumWithOneHook: {arrNum}</div>
107+
</div>
108+
);
109+
};
110+
95111
let arrNumRenderWithDepsCount = 0;
96112
const ArrNumRendererWithDeps = () => {
97113
arrNumRenderWithDepsCount += 1;
@@ -110,6 +126,11 @@ describe('createAtomStore', () => {
110126
return <div>{arr0}</div>;
111127
};
112128

129+
const BadSelectorRenderer2 = () => {
130+
const arr0 = useMyTestStoreValue('arr', { selector: (v) => v[0] });
131+
return <div>{arr0}</div>;
132+
};
133+
113134
const Buttons = () => {
114135
const store = useMyTestStoreStore();
115136
return (
@@ -154,6 +175,7 @@ describe('createAtomStore', () => {
154175
<Arr0Renderer />
155176
<Arr1Renderer />
156177
<ArrNumRenderer />
178+
<ArrNumRendererWithOneHook />
157179
<ArrNumRendererWithDeps />
158180
<Buttons />
159181
</MyTestStoreProvider>
@@ -166,6 +188,7 @@ describe('createAtomStore', () => {
166188
expect(arr0RenderCount).toBe(2);
167189
expect(arr1RenderCount).toBe(2);
168190
expect(arrNumRenderCount).toBe(2);
191+
expect(arrNumRenderCountWithOneHook).toBe(2);
169192
expect(arrNumRenderWithDepsCount).toBe(2);
170193
expect(getByText('arrNum: alice')).toBeInTheDocument();
171194
expect(getByText('arrNumWithDeps: alice')).toBeInTheDocument();
@@ -177,6 +200,7 @@ describe('createAtomStore', () => {
177200
expect(arr0RenderCount).toBe(2);
178201
expect(arr1RenderCount).toBe(2);
179202
expect(arrNumRenderCount).toBe(5);
203+
expect(arrNumRenderCountWithOneHook).toBe(5);
180204
expect(arrNumRenderWithDepsCount).toBe(5);
181205
expect(getByText('arrNum: bob')).toBeInTheDocument();
182206
expect(getByText('arrNumWithDeps: bob')).toBeInTheDocument();
@@ -188,6 +212,7 @@ describe('createAtomStore', () => {
188212
expect(arr0RenderCount).toBe(2);
189213
expect(arr1RenderCount).toBe(2);
190214
expect(arrNumRenderCount).toBe(5);
215+
expect(arrNumRenderCountWithOneHook).toBe(5);
191216
expect(arrNumRenderWithDepsCount).toBe(5);
192217
expect(getByText('arrNum: bob')).toBeInTheDocument();
193218
expect(getByText('arrNumWithDeps: bob')).toBeInTheDocument();
@@ -199,6 +224,7 @@ describe('createAtomStore', () => {
199224
expect(arr0RenderCount).toBe(2);
200225
expect(arr1RenderCount).toBe(2);
201226
expect(arrNumRenderCount).toBe(5);
227+
expect(arrNumRenderCountWithOneHook).toBe(5);
202228
expect(arrNumRenderWithDepsCount).toBe(5);
203229
expect(getByText('arrNum: bob')).toBeInTheDocument();
204230
expect(getByText('arrNumWithDeps: bob')).toBeInTheDocument();
@@ -210,6 +236,7 @@ describe('createAtomStore', () => {
210236
expect(arr0RenderCount).toBe(3);
211237
expect(arr1RenderCount).toBe(2);
212238
expect(arrNumRenderCount).toBe(8);
239+
expect(arrNumRenderCountWithOneHook).toBe(8);
213240
expect(arrNumRenderWithDepsCount).toBe(8);
214241
expect(getByText('arrNum: ava')).toBeInTheDocument();
215242
expect(getByText('arrNumWithDeps: ava')).toBeInTheDocument();
@@ -224,6 +251,16 @@ describe('createAtomStore', () => {
224251
)
225252
).toThrow();
226253
});
254+
255+
it('Throw error is user does memoize selector 2', () => {
256+
expect(() =>
257+
render(
258+
<MyTestStoreProvider>
259+
<BadSelectorRenderer2 />
260+
</MyTestStoreProvider>
261+
)
262+
).toThrow();
263+
});
227264
});
228265

229266
describe('single provider', () => {

packages/jotai-x/src/createAtomStore.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import React from 'react';
22
import { getDefaultStore, useAtom, useAtomValue, useSetAtom } from 'jotai';
33
import { selectAtom, useHydrateAtoms } from 'jotai/utils';
44

5+
6+
57
import { atomWithFn } from './atomWithFn';
68
import { createAtomProvider, useAtomStore } from './createAtomProvider';
79

@@ -493,10 +495,10 @@ export interface CreateAtomStoreOptions<
493495
* Each property will have a getter and setter.
494496
*
495497
* @example
496-
* const { exampleStore, useExampleStore } = createAtomStore({ count: 1, say: 'hello' }, { name: 'example' as const })
497-
* const [count, setCount] = useExampleStore().useCountState()
498-
* const say = useExampleStore().useSayValue()
499-
* const setSay = useExampleStore().useSetSay()
498+
* const { exampleStore, useExampleStore, useExampleValue, useExampleState, useExampleSet } = createAtomStore({ count: 1, say: 'hello' }, { name: 'example' as const })
499+
* const [count, setCount] = useExampleState()
500+
* const say = useExampleValue('say')
501+
* const setSay = useExampleSet('say')
500502
* setSay('world')
501503
* const countAtom = exampleStore.atom.count
502504
*/
@@ -591,7 +593,7 @@ export const createAtomStore = <
591593
if (renderCount > infiniteRenderDetectionLimit) {
592594
throw new Error(
593595
`
594-
use<Key>Value/useValue has rendered ${infiniteRenderDetectionLimit} times in the same render.
596+
use<Key>Value/useValue/use<StoreName>Value has rendered ${infiniteRenderDetectionLimit} times in the same render.
595597
It is very likely to have fallen into an infinite loop.
596598
That is because you do not memoize the selector/equalityFn function param.
597599
Please wrap them with useCallback or configure the deps array correctly.`
@@ -1008,4 +1010,4 @@ export function useStoreAtomState<T, E, V, A extends unknown[], R>(
10081010
atom: WritableAtom<V, A, R>
10091011
) {
10101012
return store.useAtomState(atom);
1011-
}
1013+
}

0 commit comments

Comments
 (0)