8000 feat(useAsyncState): add isomorphic destructure by Arkeviz · Pull Request #4803 · vueuse/vueuse · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

feat(useAsyncState): add isomorphic destructure #4803

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
11 changes: 11 additions & 0 deletions packages/core/useAsyncState/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,27 @@ category: State

Reactive async state. Will not block your setup function and will trigger changes once the promise is ready. The state is a `shallowRef` by default.

Also supports isomorphic destructuring via [makeDestructurable](https://vueuse.org/shared/makeDestructurable) utility.

## Usage

```ts
import { useAsyncState } from '@vueuse/core'
import axios from 'axios'

// Object destructure example
const { state, isReady, isLoading } = useAsyncState(
axios
.get('https://jsonplaceholder.typicode.com/todos/1')
.then(t => t.data),
{ id: null },
)

// Array destructure example (in right order)
const [state, execute, isLoading, isReady, error] = useAsyncState(
axios
.get('https://jsonplaceholder.typicode.com/todos/1')
.then(t => t.data),
{ id: null },
)
```
18 changes: 17 additions & 1 deletion packages/core/useAsyncState/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe('useAsyncState', () => {
})

const p1 = (num = 1) => {
return new Promise((resolve) => {
return new Promise<number>((resolve) => {
setTimeout(() => {
resolve(num)
}, 50)
Expand Down Expand Up @@ -83,4 +83,20 @@ describe('useAsyncState', () => {
const { execute } = useAsyncState(p2, '0', { throwError: true, immediate: false })
await expect(execute()).rejects.toThrowError('error')
})

describe('useAsyncState with array destructing', () => {
it('should work', async () => {
const [state, execute] = useAsyncState(p1, 0, { immediate: false })
expect(state.value).toBe(0)
await execute(0, 2)
expect(state.value).toBe(2)
})

it('should work with error', async () => {
const [_state, execute, _isLoading, _isReady, error] = useAsyncState(p2, '0', { immediate: false })
expect(error.value).toBeUndefined()
await execute()
expect(error.value).toBeInstanceOf(Error)
})
})
})
52 changes: 39 additions & 13 deletions packages/core/useAsyncState/index.ts
Original file line number Diff line number Diff lin 8000 e change
@@ -1,18 +1,32 @@
import type { Ref, ShallowRef, UnwrapRef } from 'vue'
import { noop, promiseTimeout, until } from '@vueuse/shared'
import { makeDestructurable, noop, promiseTimeout, until } from '@vueuse/shared'
import { ref as deepRef, shallowRef } from 'vue'

export interface UseAsyncStateReturnBase<Data, Params extends any[], Shallow extends boolean> {
export interface UseAsyncStateReturnBase<Data, Params extends any[], Shallow extends boolean> extends Record<string, unknown> {
state: Shallow extends true ? Ref<Data> : Ref<UnwrapRef<Data>>
isReady: Ref<boolean>
isLoading: Ref<boolean>
error: Ref<unknown>
execute: (delay?: number, ...args: Params) => Promise<Data>
}

type UseAsyncStatePromiseLike<Data, Params extends any[], Shallow extends boolean> = PromiseLike<UseAsyncStateReturn<Data, Params, Shallow>>

export type UseAsyncStateReturnArray<Data, Params extends any[], Shallow extends boolean> = [
state: UseAsyncStateReturnBase<Data, Params, Shallow>['state'],
execute: UseAsyncStateReturnBase<Data, Params, Shallow>['execute'],
isLoading: UseAsyncStateReturnBase<Data, Params, Shallow>['isLoading'],
isReady: UseAsyncStateReturnBase<Data, Params, Shallow>['isReady'],
error: UseAsyncStateReturnBase<Data, Params, Shallow>['error'],
]

export type UseAsyncStateReturn<Data, Params extends any[], Shallow extends boolean> =
UseAsyncStateReturnBase<Data, Params, Shallow>
& PromiseLike<UseAsyncStateReturnBase<Data, Params, Shallow>>
& UseAsyncStateReturnArray<Data, Params, Shallow>

export type UseAsyncStateReturnWithPromiseLike<Data, Params extends any[], Shallow extends boolean> =
UseAsyncStateReturn<Data, Params, Shallow>
& UseAsyncStatePromiseLike<Data, Params, Shallow>

export interface UseAsyncStateOptions<Shallow extends boolean, D = any> {
/**
Expand Down Expand Up @@ -82,7 +96,7 @@ export function useAsyncState<Data, Params extends any[] = any[], Shallow extend
promise: Promise<Data> | ((...args: Params) => Promise<Data>),
initialState: Data,
options?: UseAsyncStateOptions<Shallow, Data>,
): UseAsyncStateReturn<Data, Params, Shallow> {
): UseAsyncStateReturnWithPromiseLike<Data, Params, Shallow> {
const {
immediate = true,
delay = 0,
Expand Down Expand Up @@ -141,18 +155,30 @@ export function useAsyncState<Data, Params extends any[] = any[], Shallow extend
error,
execute,
}
const arrayShell: UseAsyncStateReturnArray<Data, Params, Shallow> = [
state as Shallow extends true ? ShallowRef<Data> : Ref<UnwrapRef<Data>>,
execute,
isLoading,
isReady,
error,
]

type UseAsyncStateThenParams = Parameters<UseAsyncStatePromiseLike<Data, Params, Shallow>['then']>
type UseAsyncStateThenReturn = ReturnType<UseAsyncStatePromiseLike<Data, Params, Shallow>['then']>
function then(onFulfilled: UseAsyncStateThenParams[0], onRejected: UseAsyncStateThenParams[1]): UseAsyncStateThenReturn {
return waitUntilIsLoaded()
.then(onFulfilled, onRejected)
}

function waitUntilIsLoaded() {
return new Promise<UseAsyncStateReturnBase<Data, Params, Shallow>>((resolve, reject) => {
until(isLoading).toBe(false).then(() => resolve(shell)).catch(reject)
return new Promise<UseAsyncStateReturn<Data, Params, Shallow>>((resolve, reject) => {
until(isLoading).toBe(false).then(
() => resolve(
makeDestructurable(shell, arrayShell),
),
).catch(reject)
})
}

return {
...shell,
then(onFulfilled, onRejected) {
return waitUntilIsLoaded()
.then(onFulfilled, onRejected)
},
}
return makeDestructurable({ ...shell, then }, arrayShell) as UseAsyncStateReturnWithPromiseLike<Data, Params, Shallow>
}
0