Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate reatomAsyncReaction in favor of reatomReactiveAsync #649

Merged
merged 5 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions packages/async/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,7 @@ const reatomResource = (initState, url, concurrent = true) => {

Check the real-world example in pooling example from [story tests below](https://www.reatom.dev/package/async#story-test) ([src](https://github.com/artalar/reatom/blob/v3/packages/async/src/index.story.test.ts)).

## reatomAsyncReaction
## reatomResource

This method is the simplest solution to describe an asynchronous resource that is based on local states. Let's delve into the problem.

Expand All @@ -660,12 +660,12 @@ onConnect(fetchList.dataAtom, (ctx) => fetchList(ctx, ctx.get(pageAtom)))
pageAtom.onChange(fetchSuggestion) // trigger
```

`reatomAsyncReaction` allows us to use `ctx.spy` just like in the regular `atom`. It is much simpler, more obvious, and works automatically for both caching and previous request cancellation.
`reatomResource` allows us to use `ctx.spy` just like in the regular `atom`. It is much simpler, more obvious, and works automatically for both caching and previous request cancellation.

```ts
import { reatomAsyncReaction } from '@reatom/async'
import { reatomResource } from '@reatom/async'

const listReaction = reatomAsyncReaction(async (ctx) => {
const listReaction = reatomResource(async (ctx) => {
const page = ctx.spy(pageAtom)
return request(`/api/list?page=${page}`, ctx.controller)
}, 'listReaction')
Expand All @@ -675,19 +675,23 @@ Now, `listReaction` has a `promiseAtom` that you can use with [useAtomPromise](h

If you need to set up a default value and have the ability to use the resulting data, simply use `withDataAtom` as you would with any other async action.

But that's not all! The most powerful feature of `reatomAsyncReaction` is that you can use one `promiseAtom` in another, which greatly simplifies dependent request descriptions and prevents complex race conditions, as the stale promises are always automatically canceled.
But that's not all! The most powerful feature of `reatomResource` is that you can use one `promiseAtom` in another, which greatly simplifies dependent request descriptions and prevents complex race conditions, as the stale promises are always automatically canceled.

```ts
import { reatomAsyncReaction } from '@reatom/async'
import { reatomResource } from '@reatom/async'

const aReaction = reatomAsyncReaction(async (ctx) => {
const aReaction = reatomResource(async (ctx) => {
const page = ctx.spy(pageAtom)
return request(`/api/a?page=${page}`, ctx.controller)
}, 'aReaction')
const bReaction = reatomAsyncReaction(async (ctx) => {
const bReaction = reatomResource(async (ctx) => {
const a = ctx.spy(aReaction.promiseAtom)
return request(`/api/b?a=${a}`, ctx.controller)
}, 'bReaction')
```

In this example, `bReaction.pendingAtom` will be updated immediately as `aReaction` starts fetching!

## reatomAsyncReaction

> Deprecated: use [reatomResource](#reatomresource) instead
2 changes: 1 addition & 1 deletion packages/async/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import './index.story.test'
import './withCache.test'
import './withStatusesAtom.test'
import './mapToAsync.test'
import './reatomAsyncReaction.test'
import './reatomResource.test'

test(`base API`, async () => {
const fetchData = reatomAsync(async (ctx, v: number) => {
Expand Down
7 changes: 6 additions & 1 deletion packages/async/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ export type {
AsyncStatuses,
AsyncStatusesAtom,
} from './withStatusesAtom'
export { reatomAsyncReaction, AsyncReaction } from './reatomAsyncReaction'
export {
reatomAsyncReaction,
AsyncReaction,
ResourceAtom,
reatomResource,
} from './reatomResource'

export interface AsyncAction<Params extends any[] = any[], Resp = any>
extends Action<Params, ControlledPromise<Resp>> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@ import { suite } from 'uvu'
import * as assert from 'uvu/assert'
import { createTestCtx, mockFn } from '@reatom/testing'
import { atom } from '@reatom/core'
import { reatomAsyncReaction } from './reatomAsyncReaction'
import { reatomResource } from './reatomResource'
import { noop, sleep } from '@reatom/utils'
import { withCache, withDataAtom, withErrorAtom, withRetry } from '.'
import { isConnected } from '@reatom/hooks'

export const test = suite('reatomAsyncReaction')
export const test = suite('reatomResource')

test('base', async () => {
const paramsAtom = atom(0, 'paramsAtom')
const async1 = reatomAsyncReaction(async (ctx) => {
const async1 = reatomResource(async (ctx) => {
const argument = ctx.spy(paramsAtom)
await ctx.schedule(() => sleep())
return argument
}, 'async1').promiseAtom
const async2 = reatomAsyncReaction(async (ctx) => {
const async2 = reatomResource(async (ctx) => {
const n = await ctx.spy(async1)
return n
}, 'async2').promiseAtom
Expand All @@ -40,12 +40,12 @@ test('base', async () => {
test('withCache', async () => {
const sleepTrack = mockFn(sleep)
const paramsAtom = atom(0, 'paramsAtom')
const aAtom = reatomAsyncReaction(async (ctx) => {
const aAtom = reatomResource(async (ctx) => {
const argument = ctx.spy(paramsAtom)
await ctx.schedule(() => sleepTrack())
return argument
}, 'aAtom').pipe(withCache())
const bAtom = reatomAsyncReaction(async (ctx) => {
const bAtom = reatomResource(async (ctx) => {
const n = await ctx.spy(aAtom.promiseAtom)
return n
}, 'bAtom')
Expand Down Expand Up @@ -77,7 +77,7 @@ test('withCache', async () => {
test('controller', async () => {
const controllerTrack = mockFn()
const paramsAtom = atom(0, 'paramsAtom')
const someReaction = reatomAsyncReaction(async (ctx) => {
const someReaction = reatomResource(async (ctx) => {
const argument = ctx.spy(paramsAtom)
ctx.controller.signal.addEventListener('abort', controllerTrack)
await ctx.schedule(() => sleep())
Expand All @@ -103,7 +103,7 @@ test('controller', async () => {

test('withDataAtom', async () => {
const paramsAtom = atom(0, 'paramsAtom')
const someReaction = reatomAsyncReaction(async (ctx) => {
const someReaction = reatomResource(async (ctx) => {
const params = ctx.spy(paramsAtom)
await ctx.schedule(() => sleep())
return params
Expand All @@ -121,7 +121,7 @@ test('withDataAtom', async () => {
test('withErrorAtom withRetry', async () => {
let shouldThrow = true
const paramsAtom = atom(123, 'paramsAtom')
const someReaction = reatomAsyncReaction(async (ctx) => {
const someReaction = reatomResource(async (ctx) => {
const params = ctx.spy(paramsAtom)
if (shouldThrow) throw new Error('test error')
await ctx.schedule(() => sleep())
Expand Down Expand Up @@ -155,7 +155,7 @@ test('withErrorAtom withRetry', async () => {

test('abort should not stale', async () => {
const paramsAtom = atom(123, 'paramsAtom')
const someReaction = reatomAsyncReaction(async (ctx) => {
const someReaction = reatomResource(async (ctx) => {
const params = ctx.spy(paramsAtom)
await ctx.schedule(() => sleep())
return params
Expand All @@ -172,7 +172,7 @@ test('abort should not stale', async () => {

test('direct retry', async () => {
const paramsAtom = atom(123, 'paramsAtom')
const someReaction = reatomAsyncReaction(async (ctx) => {
const someReaction = reatomResource(async (ctx) => {
ctx.spy(paramsAtom)
await ctx.schedule(() => calls++)
}, 'someReaction')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@ import { merge, noop, toAbortError } from '@reatom/utils'
import { reatomAsync, AsyncAction, ControlledPromise, AsyncCtx } from '.'
import { isConnected, onConnect } from '@reatom/hooks'

export interface AsyncReaction<Resp> extends AsyncAction<[], Resp> {
export interface ResourceAtom<Resp> extends AsyncAction<[], Resp> {
promiseAtom: Atom<ControlledPromise<Resp>>
}

/**
* @deprecated use ResourceAtom instead
*/
export interface AsyncReaction<Resp> extends ResourceAtom<Resp> {}

export interface AsyncCtxSpy extends AsyncCtx {
spy: {
<T>(anAtom: Atom<T>): T
Expand All @@ -25,10 +30,10 @@ export interface AsyncCtxSpy extends AsyncCtx {

const resolved = new Set<Promise<any>>()

export const reatomAsyncReaction = <T>(
export const reatomResource = <T>(
asyncComputed: (ctx: AsyncCtxSpy) => Promise<T>,
name = __count('asyncAtom'),
): AsyncReaction<T> => {
): ResourceAtom<T> => {
const promises = new CauseContext<Promise<any>>()

const dropCache = (ctx: Ctx) =>
Expand Down Expand Up @@ -156,7 +161,7 @@ export const reatomAsyncReaction = <T>(
},
theAsync,
{ promiseAtom },
) as AsyncReaction<T>
) as ResourceAtom<T>

Object.defineProperty(theAsync, '_handleCache', {
get() {
Expand All @@ -166,3 +171,8 @@ export const reatomAsyncReaction = <T>(

return theReaction
}

/**
* @deprecated use reatomResource instead
*/
export const reatomAsyncReaction = reatomResource
6 changes: 3 additions & 3 deletions packages/npm-react/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,14 +264,14 @@ export const MyForm = () => {

## Use atom promise

If you have an atom with a promise and want to use its value directly, you could use `useAtomPromise`. This function relies on [React Suspense](https://react.dev/reference/react/Suspense) and throws the promise until it resolves. It can be useful with [reatomAsyncReaction](https://www.reatom.dev/package/async/#reatomasyncreaction).
If you have an atom with a promise and want to use its value directly, you could use `useAtomPromise`. This function relies on [React Suspense](https://react.dev/reference/react/Suspense) and throws the promise until it resolves. It can be useful with [reatomResource](https://www.reatom.dev/package/async/#reatomresource).

```tsx
import { atom, reatomAsyncReaction } from '@reatom/framework'
import { atom, reatomResource } from '@reatom/framework'
import { useAtom, useAction, useAtomPromise } from '@reatom/npm-react'

const pageAtom = atom(1, 'pageAtom')
const listReaction = reatomAsyncReaction(async (ctx) => {
const listReaction = reatomResource(async (ctx) => {
const page = ctx.spy(pageAtom)
const response = await ctx.schedule(() => fetch(`/api/list?page=${page}`))
if (!response.ok) throw new Error(response.statusText)
Expand Down
Loading