-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs(suspensive.org): add docs for @suspensive/jotai (#1084)
# Overview ### Roadmap - [x] motivation - [x] installation - [x] Atom - [x] AtomValue - [x] SetAtom - [x] Translate ## 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. ---------
- Loading branch information
Showing
14 changed files
with
594 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
import { Callout } from '@/components' | ||
|
||
# Atom | ||
|
||
<Callout type='experimental'> | ||
|
||
`<Atom/>` is an experimental feature, so this interface may change. | ||
|
||
</Callout> | ||
|
||
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. | ||
|
||
### props.atom | ||
|
||
You can use Jotai's atom as is. | ||
|
||
```tsx /Atom/ | ||
import { Atom } from '@suspensive/jotai' | ||
import { atom } from "jotai"; | ||
|
||
const countAtom = atom(1); | ||
|
||
const Example = () => ( | ||
<Atom atom={countAtom}> | ||
{([count, setCount]) => ( | ||
<> | ||
<div>count: {count}</div> | ||
<button onClick={() => setCount(count + 1)}>+1</button> | ||
</> | ||
)} | ||
</Atom> | ||
) | ||
``` | ||
|
||
For Async Atom, it delegates the pending state of the Promise to the parent Suspense until the Promise resolves. | ||
|
||
```tsx /Atom/ | ||
import { Atom } from '@suspensive/jotai' | ||
import { Suspense } from '@suspensive/react' | ||
import { atom } from "jotai"; | ||
|
||
const countAtom = atom(1) | ||
const asyncDoubleCountAtom = atom(async (get) => { | ||
await delay(2000) | ||
return get(countAtom) * 2 | ||
}) | ||
|
||
const Example = () => ( | ||
<Suspense fallback={'pending...'}> | ||
<Atom atom={asyncDoubleCountAtom}> | ||
{([count]) => ( | ||
<>count: {count}</> | ||
)} | ||
</Atom> | ||
</Suspense> | ||
) | ||
``` | ||
|
||
### Motivation | ||
|
||
It's not immediately clear from the parent component which atoms are used internally in the child components and whether they trigger Suspense. | ||
|
||
```tsx | ||
// payment/page.tsx | ||
import { Atom } from '@suspensive/jotai' | ||
import { Suspense } from '@suspensive/react' | ||
import { UserInfo, ShoppingCart } from '~/components' | ||
|
||
const PaymentPage = () => ( | ||
<Suspense fallback={'pending...'}> | ||
<UserInfo /> {/* It's not clear whether UserInfo internally triggers Suspense with an Async Atom. */} | ||
<ShoppingCart /> {/* It's not clear whether ShoppingCart internally triggers Suspense with an Async Atom. */} | ||
</Suspense> | ||
) | ||
``` | ||
|
||
```tsx | ||
// payment/components/UserInfo.tsx | ||
import { useAtomValue } from '@suspensive/jotai' | ||
import { Suspense } from '@suspensive/react' | ||
import { UserAddress } from '~/components' | ||
import { userAsyncAtom } from "~/atoms"; | ||
|
||
// It's not clear from the usage of this component what Atom UserInfo uses internally and whether it triggers Suspense. | ||
const UserInfo = () => { | ||
const data = useAtomValue(userAsyncAtom) | ||
|
||
return <UserAddress {...data} /> | ||
} | ||
``` | ||
|
||
```tsx | ||
// payment/components/ShoppingCart.tsx | ||
import { Atom } from '@suspensive/jotai' | ||
import { Suspense } from '@suspensive/react' | ||
import { shoppingCartAtom } from "~/atoms"; | ||
|
||
// It's not clear from the usage of this component what Atom ShoppingCart uses internally and whether it triggers Suspense. | ||
const ShoppingCart = () => { | ||
const [data] = useAtom(shoppingCartAtom) | ||
|
||
return ( | ||
<> | ||
{data.map(item => <ShoppingItem key={item.id} {...item} />)} | ||
</> | ||
) | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
import { Callout } from '@/components' | ||
|
||
# Atom | ||
|
||
<Callout type='experimental'> | ||
|
||
`<Atom/>`는 실험 기능이므로 이 인터페이스는 변경될 수 있습니다. | ||
|
||
</Callout> | ||
|
||
Atom 컴포넌트는 Jotai의 [useAtom](https://jotai.org/docs/core/use-atom#useatom) 훅과 동일한 인터페이스를 Props로 제공하며 선언적으로 사용할 수 있습니다. | ||
|
||
### props.atom | ||
|
||
Jotai의 atom을 그대로 사용할 수 있습니다. | ||
|
||
```tsx /Atom/ | ||
import { Atom } from '@suspensive/jotai' | ||
import { atom } from "jotai"; | ||
|
||
const countAtom = atom(1); | ||
|
||
const Example = () => ( | ||
<Atom atom={countAtom}> | ||
{([count, setCount]) => ( | ||
<> | ||
<div>count: {count}</div> | ||
<button onClick={() => setCount(count + 1)}>+1</button> | ||
</> | ||
)} | ||
</Atom> | ||
) | ||
``` | ||
|
||
Async Atom일 경우 프로미스(Promise)가 해결(resolve) 되기 전까지, 부모의 Suspense에게 Promise의 pending 상태를 위임합니다. | ||
|
||
```tsx /Atom/ | ||
import { Atom } from '@suspensive/jotai' | ||
import { Suspense } from '@suspensive/react' | ||
import { atom } from "jotai"; | ||
|
||
const countAtom = atom(1) | ||
const asyncDoubleCountAtom = atom(async (get) => { | ||
await delay(2000) | ||
return get(countAtom) * 2 | ||
}) | ||
|
||
const Example = () => ( | ||
<Suspense fallback={'pending...'}> | ||
<Atom atom={asyncDoubleCountAtom}> | ||
{([count]) => ( | ||
<>count: {count}</> | ||
)} | ||
</Atom> | ||
</Suspense> | ||
) | ||
``` | ||
|
||
### 동기 | ||
|
||
상위 컴포넌트에서 useAtom이 명확하게 드러나지 않습니다. 하위에 선언된 컴포넌트가 내부적으로 어떤 Atom을 사용하고, Suspense를 발생할지 예상하기 어렵습니다. | ||
|
||
```tsx | ||
// payment/page.tsx | ||
import { Atom } from '@suspensive/jotai' | ||
import { Suspense } from '@suspensive/react' | ||
import { UserInfo, ShoppingCart } from '~/components' | ||
|
||
const PaymentPage = () => ( | ||
<Suspense fallback={'pending...'}> | ||
<UserInfo /> {/* UserInfo의 내부적으로 Suspense를 발생시키는 Async Atom을 사용하는지 예상하기 어렵습니다.*/} | ||
<ShoppingCart /> {/* ShoppingCart의 내부적으로 Suspense를 발생시키는 Async Atom을 사용하는지 예상하기 어렵습니다. */} | ||
</Suspense> | ||
) | ||
``` | ||
|
||
```tsx | ||
// payment/components/UserInfo.tsx | ||
import { useAtomValue } from '@suspensive/jotai' | ||
import { Suspense } from '@suspensive/react' | ||
import { UserAddress } from '~/components' | ||
import { userAsyncAtom } from "~/atoms"; | ||
|
||
// 이 컴포넌트를 사용하는 입장에서 UserInfo라는 이름만으로 내부적으로 어떤 Atom을 사용하고, Suspense를 발생할지 예상하기 어렵습니다. | ||
const UserInfo = () => { | ||
const data = useAtomValue(userAsyncAtom) | ||
|
||
return <UserAddress {...data} /> | ||
} | ||
``` | ||
|
||
```tsx | ||
// payment/components/ShoppingCart.tsx | ||
import { Atom } from '@suspensive/jotai' | ||
import { Suspense } from '@suspensive/react' | ||
import { shoppingCartAtom } from "~/atoms"; | ||
|
||
// 이 컴포넌트를 사용하는 입장에서 ShoppingCart라는 이름만으로 내부적으로 어떤 Atom을 사용하고, Suspense를 발생할지 예상하기 어렵습니다. | ||
const ShoppingCart = () => { | ||
const [data] = useAtom(shoppingCartAtom) | ||
|
||
return ( | ||
<> | ||
{data.map(item => <ShoppingItem key={item.id} {...item} />)} | ||
</> | ||
) | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { Callout } from '@/components' | ||
|
||
# AtomValue | ||
|
||
<Callout type='experimental'> | ||
|
||
`<AtomValue/>` is an experimental feature, so this interface may change. | ||
|
||
</Callout> | ||
|
||
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. | ||
|
||
### props.atom | ||
|
||
You can use Jotai's atom as is. | ||
|
||
```tsx /AtomValue/ | ||
import { AtomValue } from '@suspensive/jotai' | ||
import { atom } from "jotai"; | ||
|
||
const countAtom = atom(1); | ||
|
||
const Example = () => ( | ||
<AtomValue atom={countAtom}> | ||
{(count) => ( | ||
<>count: {count}</> | ||
)} | ||
</AtomValue> | ||
) | ||
``` | ||
|
||
For Async Atom, it delegates the pending state of the Promise to the parent Suspense until the Promise resolves. | ||
|
||
```tsx /AtomValue/ | ||
import { AtomValue } from '@suspensive/jotai' | ||
import { Suspense } from '@suspensive/react' | ||
import { atom } from "jotai"; | ||
|
||
const countAtom = atom(1) | ||
const asyncDoubleCountAtom = atom(async (get) => { | ||
await delay(2000) | ||
return get(countAtom) * 2 | ||
}) | ||
|
||
const Example = () => ( | ||
<Suspense fallback={'pending...'}> | ||
<AtomValue atom={asyncDoubleCountAtom}> | ||
{(count) => ( | ||
<>count: {count}</> | ||
)} | ||
</AtomValue> | ||
</Suspense> | ||
) | ||
``` | ||
|
||
### Motivation | ||
|
||
Similar to [`<Atom/>`](https://suspensive.org/docs/jotai/Atom), `<AtomValue/>` also does not clearly reveal which atoms are used internally in child components and whether they trigger Suspense. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { Callout } from '@/components' | ||
|
||
# AtomValue | ||
|
||
<Callout type='experimental'> | ||
|
||
`<AtomValue/>`는 실험 기능이므로 이 인터페이스는 변경될 수 있습니다. | ||
|
||
</Callout> | ||
|
||
AtomValue 컴포넌트는 Jotai의 [useAtomValue](https://jotai.org/docs/core/use-atom#useatomvalue) 훅과 동일한 인터페이스를 Props로 제공하며 선언적으로 사용할 수 있습니다. | ||
|
||
### props.atom | ||
|
||
Jotai의 atom을 그대로 사용할 수 있습니다. | ||
|
||
```tsx /AtomValue/ | ||
import { AtomValue } from '@suspensive/jotai' | ||
import { atom } from "jotai"; | ||
|
||
const countAtom = atom(1); | ||
|
||
const Example = () => ( | ||
<AtomValue atom={countAtom}> | ||
{(count) => ( | ||
<>count: {count}</> | ||
)} | ||
</AtomValue> | ||
) | ||
``` | ||
|
||
Async Atom일 경우 프로미스(Promise)가 해결(resolve) 되기 전까지, 부모의 Suspense에게 Promise의 pending 상태를 위임합니다. | ||
|
||
```tsx /AtomValue/ | ||
import { AtomValue } from '@suspensive/jotai' | ||
import { Suspense } from '@suspensive/react' | ||
import { atom } from "jotai"; | ||
|
||
const countAtom = atom(1) | ||
const asyncDoubleCountAtom = atom(async (get) => { | ||
await delay(2000) | ||
return get(countAtom) * 2 | ||
}) | ||
|
||
const Example = () => ( | ||
<Suspense fallback={'pending...'}> | ||
<AtomValue atom={asyncDoubleCountAtom}> | ||
{(count) => ( | ||
<>count: {count}</> | ||
)} | ||
</AtomValue> | ||
</Suspense> | ||
) | ||
``` | ||
|
||
### 동기 | ||
|
||
[`<Atom/>`](https://suspensive.org/ko/docs/jotai/Atom)의 동기와 같이 `<AtomValue/>` 또한, 상위 컴포넌트에서 명확하게 드러나지 않습니다. 하위에 선언된 컴포넌트가 내부적으로 어떤 Atom을 사용하고, Suspense를 발생할지 예상하기 어렵습니다. |
Oops, something went wrong.