Skip to content

Commit 4b9acdb

Browse files
gwansikkmanudeli
andauthored
feat(jotai): escape version 0 (#1205)
# Overview The changes were not significant, so I included them in a single PR. If you think the PR title is inappropriate, please feel free to change it. - Improve type inference for atom - Improved to ensure the same type inference as the original Jotai package. - Add test cases - Remove experimental tag - in suspensive.org docs - in jotai interface I have tested in the local environment to ensure that it behaves the same as Jotai (across all features). Fortunately, it works well, and I believe it is production-ready, just like Jotai. With these improvements, I think the Jotai package is ready to be released as version `1.0.0`. ## PR Checklist - [x] I did below actions if need 1. I read the [Contributing Guide](https://github.com/toss/suspensive/blob/main/CONTRIBUTING.md) 2. I added documents and tests. --------- Co-authored-by: Jonghyeon Ko <[email protected]>
1 parent e446179 commit 4b9acdb

17 files changed

+189
-85
lines changed

.changeset/config.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"@suspensive/react",
99
"@suspensive/react-query",
1010
"@suspensive/react-query-4",
11-
"@suspensive/react-query-5"
11+
"@suspensive/react-query-5",
12+
"@suspensive/jotai"
1213
]
1314
],
1415
"ignore": [

.changeset/gentle-moles-wink.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@suspensive/jotai": minor
3+
---
4+
5+
feat(jotai): escape version 0

docs/suspensive.org/src/pages/docs/jotai/Atom.en.mdx

-6
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,6 @@ import { Callout } from '@/components'
22

33
# Atom
44

5-
<Callout type='experimental'>
6-
7-
`<Atom/>` is an experimental feature, so this interface may change.
8-
9-
</Callout>
10-
115
The Atom component provides an interface similar to Jotai's [useAtom](https://jotai.org/docs/core/use-atom#useatom) hook as props, allowing declarative usage.
126

137
### props.atom

docs/suspensive.org/src/pages/docs/jotai/Atom.ko.mdx

-6
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,6 @@ import { Callout } from '@/components'
22

33
# Atom
44

5-
<Callout type='experimental'>
6-
7-
`<Atom/>`는 실험 기능이므로 이 인터페이스는 변경될 수 있습니다.
8-
9-
</Callout>
10-
115
Atom 컴포넌트는 Jotai의 [useAtom](https://jotai.org/docs/core/use-atom#useatom) 훅과 동일한 인터페이스를 Props로 제공하며 선언적으로 사용할 수 있습니다.
126

137
### props.atom

docs/suspensive.org/src/pages/docs/jotai/AtomValue.en.mdx

-6
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,6 @@ import { Callout } from '@/components'
22

33
# AtomValue
44

5-
<Callout type='experimental'>
6-
7-
`<AtomValue/>` is an experimental feature, so this interface may change.
8-
9-
</Callout>
10-
115
The AtomValue component provides an interface similar to Jotai's [useAtomValue](https://jotai.org/docs/core/use-atom#useatomvalue) hook as props, allowing declarative usage.
126

137
### props.atom

docs/suspensive.org/src/pages/docs/jotai/AtomValue.ko.mdx

-6
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,6 @@ import { Callout } from '@/components'
22

33
# AtomValue
44

5-
<Callout type='experimental'>
6-
7-
`<AtomValue/>`는 실험 기능이므로 이 인터페이스는 변경될 수 있습니다.
8-
9-
</Callout>
10-
115
AtomValue 컴포넌트는 Jotai의 [useAtomValue](https://jotai.org/docs/core/use-atom#useatomvalue) 훅과 동일한 인터페이스를 Props로 제공하며 선언적으로 사용할 수 있습니다.
126

137
### props.atom

docs/suspensive.org/src/pages/docs/jotai/SetAtom.en.mdx

-6
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,6 @@ import { Callout } from '@/components'
22

33
# SetAtom
44

5-
<Callout type='experimental'>
6-
7-
`<SetAtom/>` is an experimental feature, so this interface may change.
8-
9-
</Callout>
10-
115
The SetAtom component provides an interface similar to Jotai's [useSetAtom](https://jotai.org/docs/core/use-atom#usesetatom) hook as props, allowing declarative usage.
126

137
### props.atom

docs/suspensive.org/src/pages/docs/jotai/SetAtom.ko.mdx

-6
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,6 @@ import { Callout } from '@/components'
22

33
# SetAtom
44

5-
<Callout type='experimental'>
6-
7-
`<SetAtom/>`는 실험 기능이므로 이 인터페이스는 변경될 수 있습니다.
8-
9-
</Callout>
10-
115
SetAtom 컴포넌트는 Jotai의 [useSetAtom](https://jotai.org/docs/core/use-atom#usesetatom) 훅과 동일한 인터페이스를 Props로 제공하며 선언적으로 사용할 수 있습니다.
126

137
### props.atom

packages/jotai/src/Atom.test-d.tsx

+37-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
import { atom, useAtom } from 'jotai'
2+
import { loadable } from 'jotai/utils'
23
import type { ReactNode } from 'react'
34
import { Atom } from './Atom'
45

56
const countAtom = atom(0)
7+
const readOnlyCountAtom = atom((get) => get(countAtom))
8+
const writeOnlyCountAtom = atom(null, (get, set) => set(countAtom, get(countAtom) + 1))
69
const asyncAtom = atom(async () => Promise.resolve('string'))
7-
// eslint-disable-next-line @typescript-eslint/require-await
810
const asyncIncrementAtom = atom(null, async (get, set) => {
911
set(countAtom, get(countAtom) + 1)
1012
})
13+
const loadableAtom = loadable(asyncAtom)
1114

1215
describe('<Atom/>', () => {
1316
it('type check', () => {
@@ -20,6 +23,24 @@ describe('<Atom/>', () => {
2023
}}
2124
</Atom>
2225
)
26+
;() => (
27+
<Atom atom={readOnlyCountAtom}>
28+
{(result) => {
29+
const returnOfJotai = useAtom(readOnlyCountAtom)
30+
expectTypeOf(result).toEqualTypeOf<typeof returnOfJotai>()
31+
return <></>
32+
}}
33+
</Atom>
34+
)
35+
;() => (
36+
<Atom atom={writeOnlyCountAtom}>
37+
{(result) => {
38+
const returnOfJotai = useAtom(writeOnlyCountAtom)
39+
expectTypeOf(result).toEqualTypeOf<typeof returnOfJotai>()
40+
return <></>
41+
}}
42+
</Atom>
43+
)
2344
;() => (
2445
<Atom atom={asyncAtom}>
2546
{(result) => {
@@ -38,12 +59,27 @@ describe('<Atom/>', () => {
3859
}}
3960
</Atom>
4061
)
62+
;() => (
63+
<Atom atom={loadableAtom}>
64+
{(result) => {
65+
const returnOfJotai = useAtom(loadableAtom)
66+
expectTypeOf(result).toEqualTypeOf<typeof returnOfJotai>()
67+
return <></>
68+
}}
69+
</Atom>
70+
)
4171

4272
expectTypeOf(<Atom atom={countAtom}>{() => <></>}</Atom>).toEqualTypeOf<JSX.Element>()
4373
expectTypeOf(<Atom atom={countAtom}>{() => <></>}</Atom>).not.toEqualTypeOf<ReactNode>()
74+
expectTypeOf(<Atom atom={readOnlyCountAtom}>{() => <></>}</Atom>).toEqualTypeOf<JSX.Element>()
75+
expectTypeOf(<Atom atom={readOnlyCountAtom}>{() => <></>}</Atom>).not.toEqualTypeOf<ReactNode>()
76+
expectTypeOf(<Atom atom={writeOnlyCountAtom}>{() => <></>}</Atom>).toEqualTypeOf<JSX.Element>()
77+
expectTypeOf(<Atom atom={writeOnlyCountAtom}>{() => <></>}</Atom>).not.toEqualTypeOf<ReactNode>()
4478
expectTypeOf(<Atom atom={asyncAtom}>{() => <></>}</Atom>).toEqualTypeOf<JSX.Element>()
4579
expectTypeOf(<Atom atom={asyncAtom}>{() => <></>}</Atom>).not.toEqualTypeOf<ReactNode>()
4680
expectTypeOf(<Atom atom={asyncIncrementAtom}>{() => <></>}</Atom>).toEqualTypeOf<JSX.Element>()
4781
expectTypeOf(<Atom atom={asyncIncrementAtom}>{() => <></>}</Atom>).not.toEqualTypeOf<ReactNode>()
82+
expectTypeOf(<Atom atom={loadableAtom}>{() => <></>}</Atom>).toEqualTypeOf<JSX.Element>()
83+
expectTypeOf(<Atom atom={loadableAtom}>{() => <></>}</Atom>).not.toEqualTypeOf<ReactNode>()
4884
})
4985
})

packages/jotai/src/Atom.tsx

+14-25
Original file line numberDiff line numberDiff line change
@@ -12,71 +12,60 @@ import {
1212
useSetAtom,
1313
} from 'jotai'
1414
import type { ReactNode } from 'react'
15-
import type { ChildrenRenderProps } from './utility-types'
16-
type UseAtomProps<TAtom extends JotaiAtom<unknown>> = {
15+
import type { ChildrenRenderProps, SetAtom } from './utility-types'
16+
17+
type UseAtomProps<TAtom extends Parameters<typeof useAtom>[0]> = {
1718
atom: TAtom
1819
options?: Parameters<typeof useAtom>[1]
1920
}
2021

21-
/**
22-
* @experimental This is experimental feature.
23-
*/
2422
export function Atom<TValue, TArgs extends unknown[], TResult>({
2523
children,
2624
atom,
2725
options,
2826
}: UseAtomProps<WritableAtom<TValue, TArgs, TResult>> &
29-
ChildrenRenderProps<[Awaited<TValue>, (...args: TArgs) => TResult]>): ReactNode
27+
ChildrenRenderProps<[Awaited<TValue>, SetAtom<TArgs, TResult>]>): ReactNode
3028

31-
/**
32-
* @experimental This is experimental feature.
33-
*/
3429
export function Atom<TValue>({
3530
children,
3631
atom,
3732
options,
3833
}: UseAtomProps<PrimitiveAtom<TValue>> &
39-
ChildrenRenderProps<[Awaited<TValue>, (...args: [SetStateAction<TValue>]) => void]>): ReactNode
34+
ChildrenRenderProps<[Awaited<TValue>, SetAtom<[SetStateAction<TValue>], void>]>): ReactNode
4035

41-
/**
42-
* @experimental This is experimental feature.
43-
*/
4436
export function Atom<TValue>({
4537
children,
4638
atom,
4739
options,
4840
}: UseAtomProps<JotaiAtom<TValue>> & ChildrenRenderProps<[Awaited<TValue>, never]>): ReactNode
4941

50-
/**
51-
* @experimental This is experimental feature.
52-
*/
5342
export function Atom<TAtom extends WritableAtom<unknown, never[], unknown>>({
5443
children,
5544
atom,
5645
options,
5746
}: UseAtomProps<TAtom> &
5847
ChildrenRenderProps<
59-
[Awaited<ExtractAtomValue<TAtom>>, (...args: ExtractAtomArgs<TAtom>) => ExtractAtomResult<TAtom>]
48+
[Awaited<ExtractAtomValue<TAtom>>, SetAtom<ExtractAtomArgs<TAtom>, ExtractAtomResult<TAtom>>]
6049
>): ReactNode
6150

62-
/**
63-
* @experimental This is experimental feature.
64-
*/
6551
export function Atom<TAtom extends JotaiAtom<unknown>>({
6652
children,
6753
atom,
6854
options,
6955
}: UseAtomProps<TAtom> & ChildrenRenderProps<[Awaited<ExtractAtomValue<TAtom>>, never]>): ReactNode
7056

71-
/**
72-
* @experimental This is experimental feature.
73-
*/
7457
export function Atom<TValue, TArgs extends unknown[], TResult>({
7558
children,
7659
atom,
7760
options,
78-
}: UseAtomProps<JotaiAtom<unknown>> & ChildrenRenderProps<any>): ReactNode {
61+
}: UseAtomProps<JotaiAtom<TValue> | WritableAtom<TValue, TArgs, TResult>> &
62+
ChildrenRenderProps<ReturnType<typeof useAtom>>): ReactNode {
7963
return (
80-
<>{children([useAtomValue(atom, options), useSetAtom(atom as WritableAtom<TValue, TArgs, TResult>, options)])}</>
64+
<>
65+
{children([
66+
useAtomValue(atom, options),
67+
useSetAtom(atom as WritableAtom<TValue, TArgs, TResult>, options) as never,
68+
])}
69+
</>
8170
)
8271
}

packages/jotai/src/AtomValue.test-d.tsx

+64-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,46 @@
11
import { atom, useAtomValue } from 'jotai'
2+
import { loadable } from 'jotai/utils'
23
import type { ReactNode } from 'react'
34
import { AtomValue } from './AtomValue'
45

5-
const asyncAtom = atom(() => Promise.resolve('string'))
6+
const countAtom = atom(0)
7+
const readOnlyCountAtom = atom((get) => get(countAtom))
8+
const writeOnlyCountAtom = atom(null, (get, set) => set(countAtom, get(countAtom) + 1))
9+
const asyncAtom = atom(async () => Promise.resolve('string'))
10+
const asyncIncrementAtom = atom(null, async (get, set) => {
11+
set(countAtom, get(countAtom) + 1)
12+
})
13+
const loadableAtom = loadable(asyncAtom)
614

715
describe('<AtomValue/>', () => {
816
it('type check', () => {
17+
;() => (
18+
<AtomValue atom={countAtom}>
19+
{(value) => {
20+
const valueOfJotai = useAtomValue(countAtom)
21+
expectTypeOf(value).toEqualTypeOf<typeof valueOfJotai>()
22+
return <></>
23+
}}
24+
</AtomValue>
25+
)
26+
;() => (
27+
<AtomValue atom={readOnlyCountAtom}>
28+
{(value) => {
29+
const valueOfJotai = useAtomValue(readOnlyCountAtom)
30+
expectTypeOf(value).toEqualTypeOf<typeof valueOfJotai>()
31+
return <></>
32+
}}
33+
</AtomValue>
34+
)
35+
;() => (
36+
<AtomValue atom={writeOnlyCountAtom}>
37+
{(value) => {
38+
const valueOfJotai = useAtomValue(writeOnlyCountAtom)
39+
expectTypeOf(value).toEqualTypeOf<typeof valueOfJotai>()
40+
return <></>
41+
}}
42+
</AtomValue>
43+
)
944
;() => (
1045
<AtomValue atom={asyncAtom}>
1146
{(value) => {
@@ -15,8 +50,36 @@ describe('<AtomValue/>', () => {
1550
}}
1651
</AtomValue>
1752
)
53+
;() => (
54+
<AtomValue atom={asyncIncrementAtom}>
55+
{(value) => {
56+
const valueOfJotai = useAtomValue(asyncIncrementAtom)
57+
expectTypeOf(value).toEqualTypeOf<typeof valueOfJotai>()
58+
return <></>
59+
}}
60+
</AtomValue>
61+
)
62+
;() => (
63+
<AtomValue atom={loadableAtom}>
64+
{(value) => {
65+
const valueOfJotai = useAtomValue(loadableAtom)
66+
expectTypeOf(value).toEqualTypeOf<typeof valueOfJotai>()
67+
return <></>
68+
}}
69+
</AtomValue>
70+
)
1871

72+
expectTypeOf(<AtomValue atom={countAtom}>{() => <></>}</AtomValue>).toEqualTypeOf<JSX.Element>()
73+
expectTypeOf(<AtomValue atom={countAtom}>{() => <></>}</AtomValue>).not.toEqualTypeOf<ReactNode>()
74+
expectTypeOf(<AtomValue atom={readOnlyCountAtom}>{() => <></>}</AtomValue>).toEqualTypeOf<JSX.Element>()
75+
expectTypeOf(<AtomValue atom={readOnlyCountAtom}>{() => <></>}</AtomValue>).not.toEqualTypeOf<ReactNode>()
76+
expectTypeOf(<AtomValue atom={writeOnlyCountAtom}>{() => <></>}</AtomValue>).toEqualTypeOf<JSX.Element>()
77+
expectTypeOf(<AtomValue atom={writeOnlyCountAtom}>{() => <></>}</AtomValue>).not.toEqualTypeOf<ReactNode>()
1978
expectTypeOf(<AtomValue atom={asyncAtom}>{() => <></>}</AtomValue>).toEqualTypeOf<JSX.Element>()
2079
expectTypeOf(<AtomValue atom={asyncAtom}>{() => <></>}</AtomValue>).not.toEqualTypeOf<ReactNode>()
80+
expectTypeOf(<AtomValue atom={asyncIncrementAtom}>{() => <></>}</AtomValue>).toEqualTypeOf<JSX.Element>()
81+
expectTypeOf(<AtomValue atom={asyncIncrementAtom}>{() => <></>}</AtomValue>).not.toEqualTypeOf<ReactNode>()
82+
expectTypeOf(<AtomValue atom={loadableAtom}>{() => <></>}</AtomValue>).toEqualTypeOf<JSX.Element>()
83+
expectTypeOf(<AtomValue atom={loadableAtom}>{() => <></>}</AtomValue>).not.toEqualTypeOf<ReactNode>()
2184
})
2285
})

packages/jotai/src/AtomValue.tsx

+18-8
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,28 @@
1-
import { type Atom as JotaiAtom, useAtomValue } from 'jotai'
1+
import { type ExtractAtomValue, type Atom as JotaiAtom, useAtomValue } from 'jotai'
2+
import type { ReactNode } from 'react'
23
import type { ChildrenRenderProps } from './utility-types'
34

4-
type UseAtomValueProps<TValue> = {
5-
atom: JotaiAtom<TValue>
6-
options?: Parameters<typeof useAtomValue>[1] & { delay?: number }
5+
type UseAtomValueProps<TAtom extends Parameters<typeof useAtomValue>[0]> = {
6+
atom: TAtom
7+
options?: Parameters<typeof useAtomValue>[1]
78
}
89

9-
/**
10-
* @experimental This is experimental feature.
11-
*/
1210
export function AtomValue<TValue>({
1311
children,
1412
atom,
1513
options,
16-
}: UseAtomValueProps<TValue> & ChildrenRenderProps<Awaited<TValue>>) {
14+
}: UseAtomValueProps<JotaiAtom<TValue>> & ChildrenRenderProps<Awaited<TValue>>): ReactNode
15+
16+
export function AtomValue<TAtom extends JotaiAtom<unknown>>({
17+
children,
18+
atom,
19+
options,
20+
}: UseAtomValueProps<TAtom> & ChildrenRenderProps<Awaited<ExtractAtomValue<TAtom>>>): ReactNode
21+
22+
export function AtomValue<TValue>({
23+
children,
24+
atom,
25+
options,
26+
}: UseAtomValueProps<JotaiAtom<TValue>> & ChildrenRenderProps<ReturnType<typeof useAtomValue>>): ReactNode {
1727
return <>{children(useAtomValue(atom, options))}</>
1828
}

0 commit comments

Comments
 (0)