Skip to content

Commit c0ec41f

Browse files
committed
hooks
1 parent c0e6483 commit c0ec41f

14 files changed

+1414
-2027
lines changed

.changeset/cool-swans-flash.md

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
'jotai-x': minor
3+
---
4+
5+
- `createAtomStore` now returns new utility hooks to provide a more direct way to access store atoms without having to call `useStore()` first:
6+
- `use<Name>State(key, storeOptions)` - Get/set state for a specific key
7+
- `use<Name>Value(key, options, deps)` - Get value for a specific key with optional selector and equality function
8+
- `use<Name>Set(key, storeOptions)` - Get setter for a specific key

.eslintrc.cjs

+1-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ module.exports = {
1111
'./config/eslint/bases/javascript.cjs',
1212
'./config/eslint/bases/typescript.cjs',
1313
'./config/eslint/bases/regexp.cjs',
14-
'./config/eslint/bases/jest.cjs',
14+
'./config/eslint/bases/vitest.cjs',
1515
'./config/eslint/bases/react.cjs',
1616
'./config/eslint/bases/react-compiler.cjs',
1717
'./config/eslint/bases/rtl.cjs',
@@ -29,7 +29,6 @@ module.exports = {
2929
env: {
3030
browser: true,
3131
es6: true,
32-
jest: true,
3332
node: true,
3433
webextensions: false,
3534
},

config/eslint/bases/jest.cjs

-49
This file was deleted.

config/eslint/bases/unicorn.cjs

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ module.exports = {
99
checkArrowFunctions: false,
1010
},
1111
],
12+
'unicorn/prefer-module': 'off',
1213
'unicorn/expiring-todo-comments': 'off',
1314
'unicorn/filename-case': 'off',
1415
'unicorn/no-array-callback-reference': 'off',

config/eslint/bases/vitest.cjs

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
const vitest = require('@vitest/eslint-plugin');
2+
3+
module.exports = {
4+
plugins: ['vitest'],
5+
overrides: [
6+
{
7+
files: ['**/?(*.)+(test|spec).{js,jsx,ts,tsx}'],
8+
rules: {
9+
...vitest.configs.recommended.rules,
10+
'@typescript-eslint/ban-ts-comment': 'off',
11+
'@typescript-eslint/no-empty-function': 'off',
12+
'@typescript-eslint/no-explicit-any': 'off',
13+
'@typescript-eslint/no-non-null-assertion': 'off',
14+
'@typescript-eslint/no-object-literal-type-assertion': 'off',
15+
'import/default': 'off',
16+
'import/namespace': 'off',
17+
'import/no-duplicates': 'off',
18+
'import/no-named-as-default-member': 'off',
19+
},
20+
},
21+
],
22+
};

jest.config.cjs

-52
This file was deleted.

package.json

+8-9
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"workspaces": [
55
"packages/**"
66
],
7+
"type": "module",
78
"scripts": {
89
"build": "yarn g:build",
910
"build:watch": "ROARR_LOG=true turbowatch ./config/turbowatch.config.ts | roarr",
@@ -38,11 +39,11 @@
3839
"p:brl:below": "cd $INIT_CWD && barrelsby -d $INIT_CWD/src -D -l below -q -e '.*(fixture|template|spec|__tests__).*'",
3940
"p:build": "cd $INIT_CWD && yarn p:tsup",
4041
"p:build:watch": "cd $INIT_CWD && yarn p:tsup --watch",
41-
"p:clean": "cd $INIT_CWD && rimraf dist && jest --clear-cache",
42+
"p:clean": "cd $INIT_CWD && rimraf dist",
4243
"p:lint": "eslint $INIT_CWD/src --color",
4344
"p:lint:fix": "eslint $INIT_CWD/src --color --fix",
4445
"p:tsup": "cd $INIT_CWD && tsup --config=${PROJECT_CWD}/config/tsup.config.ts",
45-
"p:test": "cd $INIT_CWD && jest --config=${PROJECT_CWD}/jest.config.cjs --maxWorkers=1 --passWithNoTests $INIT_CWD ",
46+
"p:test": "cd $INIT_CWD && vitest run --config=${PROJECT_CWD}/vitest.config.ts --passWithNoTests",
4647
"p:typecheck": "cd $INIT_CWD && tsc --noEmit --emitDeclarationOnly false"
4748
},
4849
"devDependencies": {
@@ -54,18 +55,17 @@
5455
"@ianvs/prettier-plugin-sort-imports": "^4.1.1",
5556
"@roarr/cli": "^5.12.4",
5657
"@swc/core": "1.3.100",
57-
"@swc/jest": "0.2.29",
5858
"@testing-library/jest-dom": "^6.1.5",
5959
"@testing-library/react": "^14.1.2",
6060
"@testing-library/react-hooks": "^8.0.1",
6161
"@testing-library/user-event": "^14.5.1",
6262
"@types/glob": "8.1.0",
63-
"@types/jest": "^29.5.11",
6463
"@types/node": "^20.10.4",
6564
"@types/react": "18.2.42",
6665
"@types/react-dom": "18.2.17",
6766
"@typescript-eslint/eslint-plugin": "^6.13.2",
6867
"@typescript-eslint/parser": "^6.13.2",
68+
"@vitest/eslint-plugin": "1.1.25",
6969
"app-root-path": "^3.1.0",
7070
"barrelsby": "^2.8.1",
7171
"concurrently": "^8.2.2",
@@ -77,7 +77,6 @@
7777
"eslint-plugin-chai-friendly": "^0.7.2",
7878
"eslint-plugin-cypress": "^2.15.1",
7979
"eslint-plugin-import": "^2.29.0",
80-
"eslint-plugin-jest": "^27.6.0",
8180
"eslint-plugin-jsx-a11y": "^6.8.0",
8281
"eslint-plugin-mdx": "^2.2.0",
8382
"eslint-plugin-prettier": "^5.0.1",
@@ -89,21 +88,21 @@
8988
"eslint-plugin-testing-library": "^6.2.0",
9089
"eslint-plugin-unicorn": "^49.0.0",
9190
"eslint-plugin-unused-imports": "^3.0.0",
92-
"jest": "29.7.0",
93-
"jest-environment-jsdom": "^29.7.0",
91+
"eslint-plugin-vitest": "0.5.4",
9492
"jotai": "^2.6.0",
93+
"jsdom": "26.0.0",
9594
"prettier": "^3.1.0",
9695
"react": "^18.2.0",
9796
"react-dom": "^18.2.0",
9897
"react-is": "18.2.0",
9998
"react-test-renderer": "18.2.0",
10099
"rimraf": "^5.0.5",
101-
"ts-jest": "^29.1.1",
102100
"tsup": "8.0.1",
103101
"turbo": "^1.11.0",
104102
"turbowatch": "2.29.4",
105103
"typedoc": "^0.25.4",
106-
"typescript": "5.7.3"
104+
"typescript": "5.7.3",
105+
"vitest": "3.0.5"
107106
},
108107
"packageManager": "[email protected]",
109108
"engines": {

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

+29-10
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,16 @@ describe('createAtomStore', () => {
242242
becomeFriends: () => {},
243243
};
244244

245-
const { myTestStoreStore, useMyTestStoreStore, MyTestStoreProvider } =
246-
createAtomStore(initialTestStoreValue, { name: 'myTestStore' as const });
245+
const {
246+
myTestStoreStore,
247+
useMyTestStoreStore,
248+
MyTestStoreProvider,
249+
useMyTestStoreSet,
250+
useMyTestStoreState,
251+
useMyTestStoreValue,
252+
} = createAtomStore(initialTestStoreValue, {
253+
name: 'myTestStore' as const,
254+
});
247255

248256
const ReadOnlyConsumer = () => {
249257
const name = useMyTestStoreStore().useNameValue();
@@ -414,6 +422,7 @@ describe('createAtomStore', () => {
414422
// Just guarantee that the react compiler doesn't complain
415423
/* eslint-enable react-compiler/react-compiler */
416424
const store = useMyTestStoreStore();
425+
const becomeFriends0 = useMyTestStoreValue('becomeFriends');
417426
const becomeFriends1 = useStoreValue(store, 'becomeFriends');
418427
const becomeFriends2 = useStoreAtomValue(
419428
store,
@@ -424,6 +433,7 @@ describe('createAtomStore', () => {
424433
<button
425434
type="button"
426435
onClick={() => {
436+
becomeFriends0();
427437
becomeFriends1();
428438
becomeFriends2();
429439
}}
@@ -507,14 +517,18 @@ describe('createAtomStore', () => {
507517
// Just guarantee that the react compiler doesn't complain
508518
/* eslint-enable react-compiler/react-compiler */
509519
const store = useMyTestStoreStore();
520+
const setName = useMyTestStoreSet('name');
510521
const setBecomeFriends = useStoreSet(store, 'becomeFriends');
511522
const [becameFriends, setBecameFriends] = React.useState(false);
512523

513524
return (
514525
<>
515526
<button
516527
type="button"
517-
onClick={() => setBecomeFriends(() => setBecameFriends(true))}
528+
onClick={() => {
529+
setName('new');
530+
setBecomeFriends(() => setBecameFriends(true));
531+
}}
518532
>
519533
Change Callback
520534
</button>
@@ -603,16 +617,20 @@ describe('createAtomStore', () => {
603617
// Just guarantee that the react compiler doesn't complain
604618
/* eslint-enable react-compiler/react-compiler */
605619
const store = useMyTestStoreStore();
620+
const [name, setName] = useMyTestStoreState('name');
606621
const [, setBecomeFriends] = useStoreState(store, 'becomeFriends');
607622
const [becameFriends, setBecameFriends] = React.useState(false);
608623

609624
return (
610625
<>
611626
<button
612627
type="button"
613-
onClick={() => setBecomeFriends(() => setBecameFriends(true))}
628+
onClick={() => {
629+
setName('new');
630+
setBecomeFriends(() => setBecameFriends(true));
631+
}}
614632
>
615-
Change Callback
633+
Change Callback {name}
616634
</button>
617635

618636
<div>useBecameFriends: {becameFriends.toString()}</div>
@@ -791,8 +809,8 @@ describe('createAtomStore', () => {
791809
});
792810

793811
it('can subscribe', () => {
794-
const subName = jest.fn();
795-
const subAge = jest.fn();
812+
const subName = vi.fn();
813+
const subAge = vi.fn();
796814
const { getByText } = render(
797815
<MutableProvider>
798816
<SubscribeConsumer subName={subName} subAge={subAge} />
@@ -820,8 +838,8 @@ describe('createAtomStore', () => {
820838
});
821839

822840
it('can subscribe with key param', () => {
823-
const subName = jest.fn();
824-
const subAge = jest.fn();
841+
const subName = vi.fn();
842+
const subAge = vi.fn();
825843
const { getByText } = render(
826844
<MutableProvider>
827845
<SubscribeConsumerWithKeyParam subName={subName} subAge={subAge} />
@@ -1014,9 +1032,10 @@ describe('createAtomStore', () => {
10141032
</BecomeFriendsProvider>
10151033
);
10161034

1017-
act(() => getByText('Change Callback').click());
1035+
act(() => getByText('Change Callback John').click());
10181036
expect(getByText('useBecameFriends: false')).toBeInTheDocument();
10191037
act(() => getByText('Become Friends').click());
1038+
expect(getByText('Change Callback new')).toBeInTheDocument();
10201039
expect(getByText('useBecameFriends: true')).toBeInTheDocument();
10211040
});
10221041
});

0 commit comments

Comments
 (0)