Skip to content

Commit

Permalink
feat: 🎸 暴露 useQuery stream (#59)
Browse files Browse the repository at this point in the history
  • Loading branch information
WhimsFate authored Aug 24, 2023
1 parent aa835cf commit cd6f9da
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 48 deletions.
8 changes: 6 additions & 2 deletions packages/model/examples/client/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,21 @@ export const TestModel = createModel(() => {
const a = ref(1);
const b = computed(() => a.value * 2);
const c = ref(1);
const { data } = useRestQuery<{result: 1, data: number}>({
const { data, stream$ } = useRestQuery<{result: 1, data: number}>({
url: '/api',
method: 'post',
variables() {
console.error('啦啦啦啦啦');
return {
params: c.value
params: b.value
};
},
});

stream$.subscribe(sub => {
console.log(sub)
})

return {
data,
a,
Expand Down
5 changes: 3 additions & 2 deletions packages/model/src/model/fn-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const useRestQuery = <T>(options: RestQueryOptions<null, T>) => {
store.hydrationStatus,
client.rest
);
tempQueryList.push(query);
tempQueryList.push(query as ReturnType<typeof createRestQuery>);

const status = useStatus(query.info, query.requestReason);
const { loading, error, data } = toRefs(query.info);
Expand All @@ -51,7 +51,8 @@ export const useRestQuery = <T>(options: RestQueryOptions<null, T>) => {
refetch: query.refetch,
fetchMore: query.fetchMore,
onNext: query.onNext,
requestReason: query.requestReason
requestReason: query.requestReason,
stream$: query.stream$
};
};

Expand Down
121 changes: 105 additions & 16 deletions packages/model/src/operations/restQuery.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { RouteLocationNormalizedLoaded } from 'vue-router';
import { Observable, type Subscription } from 'rxjs';
import { Observable, Subject, type Subscription } from 'rxjs';

import type { RestQueryOptions, RestFetchMoreOption } from './types';
import type { Client, RestRequestConfig } from '../clients';
Expand All @@ -8,7 +8,7 @@ import type { HydrationStatus, Store } from '../store';
import { generateQueryOptions } from './core';
import { computed, ref, type Ref } from 'vue';
import { deepMerge } from '../utils';
import { RequestReason } from './status';
import { type InfoDataType, RequestReason, StateStatus, getStatus } from './status';

export function createRestQuery<ModelType, DataType>(
option: RestQueryOptions<ModelType, DataType>,
Expand All @@ -33,9 +33,16 @@ export function createRestQuery<ModelType, DataType>(
return option.url;
});


function fetch() {
info.loading = true;
return new Promise((resolve) => {
// 这里记录一下请求开始时的 info 信息,避免并发请求 status 错乱
const singleInfo = {
...info
}
const id = ++requestId
const reason = requestReason.value
singleInfo.loading = info.loading = true;
const clientParams = {
url: url.value,
headers: option.headers,
Expand All @@ -44,20 +51,55 @@ export function createRestQuery<ModelType, DataType>(
variables: variables.value,
timeout: option.timeout,
};

stream$.next({
id,
url: clientParams.url,
variables: clientParams.variables,
requestReason: requestReason.value,
status: getStatus(singleInfo, reason),
data: undefined,
loading: true,
error: undefined,
})
// TODO后面再重写一下
const subject = client!.query<DataType>(clientParams, option.fetchPolicy, hydrationStatus);
subject.subscribe({
next: (data) => {
info.error = undefined;
singleInfo.error = info.error = undefined;
if (data) {
info.data = data;
singleInfo.data = info.data = data;
}
info.loading = false;
singleInfo.loading = info.loading = false;

stream$.next({
id,
url: clientParams.url,
variables: clientParams.variables,
requestReason: requestReason.value,
status: getStatus(singleInfo, reason),
data: info.data,
loading: false,
error: undefined,
})

resolve(undefined);
},
error: (e) => {
info.error = e;
info.loading = false;
singleInfo.error = info.error = e;
singleInfo.loading = info.loading = false;

stream$.next({
id,
url: clientParams.url,
variables: clientParams.variables,
requestReason: requestReason.value,
status: getStatus(singleInfo, reason),
data: undefined,
loading: false,
error: info.error,
})

resolve(undefined);
},
complete: () => {
Expand All @@ -69,7 +111,14 @@ export function createRestQuery<ModelType, DataType>(
}

let sub: Subscription | null = null;

let requestId = 0
const stream$ = new Subject<InfoDataType<DataType> & {
id: number
url: string
variables?: Record<string, unknown>
requestReason: RequestReason
status: StateStatus,
}>()
const requestReason = ref<RequestReason>(RequestReason.setVariables);
function init() {
sub = fetchQuery$.subscribe((reason) => {
Expand All @@ -85,13 +134,19 @@ export function createRestQuery<ModelType, DataType>(
if (sub) {
sub.unsubscribe();
sub = null;
stream$.complete()
}
}

function fetchMore(variables: RestFetchMoreOption['variables']) {
return new Promise((resolve) => {
requestReason.value = RequestReason.fetchMore
info.loading = true;
// 这里记录一下请求开始时的 info 信息,避免并发请求 status 错乱
const singleInfo = {
...info
}
const id = ++requestId
const reason = requestReason.value = RequestReason.fetchMore
singleInfo.loading = info.loading = true;
const params: RestRequestConfig = {
url: url.value,
method: option.method,
Expand All @@ -103,17 +158,50 @@ export function createRestQuery<ModelType, DataType>(
params.headers = deepMerge({}, params.headers || {}, option.headers);
}

stream$.next({
id,
url: params.url,
variables: params.variables,
requestReason: requestReason.value,
status: getStatus(singleInfo, reason),
data: undefined,
loading: true,
error: undefined,
})

const observable = client!.query<DataType>(params, option.fetchPolicy, hydrationStatus);
observable.subscribe({
next: (data) => {
info.error = undefined;
info.data = data && option.updateQuery ? option.updateQuery(info.data, data) : data;
info.loading = false;
singleInfo.error = info.error = undefined;
singleInfo.data = info.data = data && option.updateQuery ? option.updateQuery(info.data, data) : data;
singleInfo.loading = info.loading = false;

stream$.next({
id,
url: params.url,
variables: params.variables,
requestReason: requestReason.value,
status: getStatus(singleInfo, reason),
data: info.data,
loading: false,
error: undefined,
})
resolve(undefined);
},
error: (e) => {
info.error = e;
info.loading = false;
singleInfo.error = info.error = e;
singleInfo.loading = info.loading = false;

stream$.next({
id,
url: params.url,
variables: params.variables,
requestReason: requestReason.value,
status: getStatus(singleInfo, reason),
data: undefined,
loading: false,
error: info.error,
})
resolve(undefined);
},
});
Expand All @@ -136,5 +224,6 @@ export function createRestQuery<ModelType, DataType>(
destroy,
onNext,
requestReason,
stream$
};
}
58 changes: 30 additions & 28 deletions packages/model/src/operations/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,36 +220,38 @@ export function assertRefreshErrorState<T>(v: StateStatus): asserts v is StateSt
}
}

export function useStatus<T>(info: InfoDataType<T>, requestReason: Ref<RequestReason>): ComputedRef<StateStatus> {
return computed(() => {
if (info.loading) {
if (!isDef(info.data)) {
return StateStatus.Loading;
}
if (requestReason.value === RequestReason.fetchMore) {
return StateStatus.FetchMore;
}
if (requestReason.value === RequestReason.poll) {
return StateStatus.Pool;
}
return StateStatus.Refresh;
export function getStatus<T>(info: InfoDataType<T>, requestReason: RequestReason): StateStatus {
if (info.loading) {
if (!isDef(info.data)) {
return StateStatus.Loading;
}
if (isDef(info.error)) {
if (!isDef(info.data)) {
return StateStatus.Error;
}
if (requestReason.value === RequestReason.fetchMore) {
return StateStatus.FetchMoreError;
}
if (requestReason.value === RequestReason.poll) {
return StateStatus.PoolError;
}
return StateStatus.RefreshError;
if (requestReason === RequestReason.fetchMore) {
return StateStatus.FetchMore;
}

if (requestReason === RequestReason.poll) {
return StateStatus.Pool;
}
return StateStatus.Refresh;
}
if (isDef(info.error)) {
if (!isDef(info.data)) {
return StateStatus.Empty;
return StateStatus.Error;
}
if (requestReason === RequestReason.fetchMore) {
return StateStatus.FetchMoreError;
}
if (requestReason === RequestReason.poll) {
return StateStatus.PoolError;
}
return StateStatus.Done;
});
return StateStatus.RefreshError;
}

if (!isDef(info.data)) {
return StateStatus.Empty;
}
return StateStatus.Done;
}

export function useStatus<T>(info: InfoDataType<T>, requestReason: Ref<RequestReason>): ComputedRef<StateStatus> {
return computed(() => getStatus(info, requestReason.value));
}

0 comments on commit cd6f9da

Please sign in to comment.