Skip to content

Commit c0b05e9

Browse files
committed
feat: add observer and useData
1 parent 8131929 commit c0b05e9

File tree

9 files changed

+224
-1
lines changed

9 files changed

+224
-1
lines changed

src/core/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ export type {
33
DataSourceTag,
44
DataSource,
55
AnyDataSource,
6+
DataObserver,
7+
DataListener,
68
DataSourceContext,
79
DataSourceParams,
810
DataSourceRequest,

src/core/types/DataSource.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ export interface DataSource<
2424
fetchContext: TFetchContext,
2525
request: TRequest,
2626
) => Promise<TResponse> | TResponse;
27+
observe: (
28+
context: TContext,
29+
params: ActualParams<this, TParams, TRequest>,
30+
options?: TOptions,
31+
) => DataObserver<this>;
2732
tags?: (params: ActualParams<this, TParams, TRequest>) => DataSourceTag[];
2833

2934
transformParams?: (params: TParams) => TRequest;
@@ -38,6 +43,19 @@ export interface DataSource<
3843
// eslint-disable-next-line @typescript-eslint/no-explicit-any
3944
export type AnyDataSource = DataSource<any, any, any, any, any, any, any, any, any>;
4045

46+
export interface DataObserver<TDataSource extends AnyDataSource> {
47+
getCurrentState(): DataSourceState<TDataSource>;
48+
updateParams(
49+
params: DataSourceParams<TDataSource>,
50+
options?: DataSourceOptions<TDataSource>,
51+
): void;
52+
subscribe(listener: DataListener<TDataSource>): () => void;
53+
}
54+
55+
export type DataListener<TDataSource extends AnyDataSource> = (
56+
state: DataSourceState<TDataSource>,
57+
) => void;
58+
4159
export type DataSourceContext<TDataSource> =
4260
TDataSource extends DataSource<
4361
infer TContext,

src/react-query/hooks/useQueryData.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import type {DataSourceOptions, DataSourceParams, DataSourceState} from '../../core';
1+
import type {
2+
DataSourceContext,
3+
DataSourceOptions,
4+
DataSourceParams,
5+
DataSourceState,
6+
} from '../../core';
7+
import {useData} from '../../react';
28
import {useInfiniteQueryData} from '../impl/infinite/hooks';
39
import {usePlainQueryData} from '../impl/plain/hooks';
410
import type {AnyQueryDataSource} from '../types';
@@ -27,3 +33,15 @@ export const useQueryData = <TDataSource extends AnyQueryDataSource>(
2733

2834
return state as DataSourceState<TDataSource>;
2935
};
36+
37+
// Do not use it yet. It will be reworked
38+
export const _useQueryData = <TDataSource extends AnyQueryDataSource>(
39+
dataSource: TDataSource,
40+
params: DataSourceParams<TDataSource>,
41+
options?: DataSourceOptions<TDataSource>,
42+
) => {
43+
const context = useQueryContext() as DataSourceContext<TDataSource>;
44+
const [state] = useData<TDataSource>(dataSource, context, params, options);
45+
46+
return state;
47+
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
import {InfiniteQueryDataObserver} from './observer';
12
import type {InfiniteQueryDataSource} from './types';
23

34
export const makeInfiniteQueryDataSource = <TParams, TRequest, TResponse, TData, TError>(
45
config: Omit<InfiniteQueryDataSource<TParams, TRequest, TResponse, TData, TError>, 'type'>,
56
): InfiniteQueryDataSource<TParams, TRequest, TResponse, TData, TError> => ({
67
...config,
78
type: 'infinite',
9+
observe(context, params, options) {
10+
return new InfiniteQueryDataObserver(context, this, params, options);
11+
},
812
});
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import type {InfiniteQueryObserverResult} from '@tanstack/react-query';
2+
import {InfiniteQueryObserver} from '@tanstack/react-query';
3+
4+
import type {
5+
DataListener,
6+
DataObserver,
7+
DataSourceContext,
8+
DataSourceData,
9+
DataSourceError,
10+
DataSourceOptions,
11+
DataSourceParams,
12+
DataSourceResponse,
13+
} from '../../../core';
14+
15+
import type {AnyInfiniteQueryDataSource} from './types';
16+
import {composeOptions, transformResult} from './utils';
17+
18+
export class InfiniteQueryDataObserver<
19+
TDataSource extends AnyInfiniteQueryDataSource,
20+
TContext extends DataSourceContext<TDataSource> = DataSourceContext<TDataSource>,
21+
TParams extends DataSourceParams<TDataSource> = DataSourceParams<TDataSource>,
22+
TResponse extends DataSourceResponse<TDataSource> = DataSourceResponse<TDataSource>,
23+
TData extends DataSourceData<TDataSource> = DataSourceData<TDataSource>,
24+
TError extends DataSourceError<TDataSource> = DataSourceError<TDataSource>,
25+
TOptions extends DataSourceOptions<TDataSource> = DataSourceOptions<TDataSource>,
26+
> implements DataObserver<TDataSource>
27+
{
28+
readonly context: TContext;
29+
readonly dataSource: TDataSource;
30+
readonly observer: InfiniteQueryObserver<TResponse, TError, TData, TResponse>;
31+
32+
constructor(context: TContext, dataSource: TDataSource, params: TParams, options?: TOptions) {
33+
this.context = context;
34+
this.dataSource = dataSource;
35+
this.observer = new InfiniteQueryObserver(
36+
context.queryClient,
37+
this.composeOptions(context, dataSource, params, options),
38+
);
39+
}
40+
41+
getCurrentState() {
42+
return this.transformResult(this.observer.getCurrentResult());
43+
}
44+
45+
updateParams(params: TParams, options?: TOptions) {
46+
this.observer.setOptions(
47+
this.composeOptions(this.context, this.dataSource, params, options),
48+
);
49+
}
50+
51+
subscribe(listener: DataListener<TDataSource>) {
52+
return this.observer.subscribe((result) => {
53+
listener(this.transformResult(result));
54+
});
55+
}
56+
57+
private composeOptions(
58+
context: TContext,
59+
dataSource: TDataSource,
60+
params: TParams,
61+
options?: TOptions,
62+
) {
63+
return composeOptions(context, dataSource, params, options);
64+
}
65+
66+
private transformResult(result: InfiniteQueryObserverResult<TData, TError>) {
67+
return transformResult<TDataSource>(result);
68+
}
69+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
import {PlainQueryDataObserver} from './observer';
12
import type {PlainQueryDataSource} from './types';
23

34
export const makePlainQueryDataSource = <TParams, TRequest, TResponse, TData, TError>(
45
config: Omit<PlainQueryDataSource<TParams, TRequest, TResponse, TData, TError>, 'type'>,
56
): PlainQueryDataSource<TParams, TRequest, TResponse, TData, TError> => ({
67
...config,
78
type: 'plain',
9+
observe(context, params, options) {
10+
return new PlainQueryDataObserver(context, this, params, options);
11+
},
812
});
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import type {QueryObserverOptions, QueryObserverResult} from '@tanstack/react-query';
2+
import {QueryObserver} from '@tanstack/react-query';
3+
4+
import type {
5+
DataListener,
6+
DataObserver,
7+
DataSourceContext,
8+
DataSourceData,
9+
DataSourceError,
10+
DataSourceOptions,
11+
DataSourceParams,
12+
DataSourceResponse,
13+
} from '../../../core';
14+
15+
import type {AnyPlainQueryDataSource} from './types';
16+
import {composeOptions, transformResult} from './utils';
17+
18+
export class PlainQueryDataObserver<
19+
TDataSource extends AnyPlainQueryDataSource,
20+
TContext extends DataSourceContext<TDataSource> = DataSourceContext<TDataSource>,
21+
TParams extends DataSourceParams<TDataSource> = DataSourceParams<TDataSource>,
22+
TResponse extends DataSourceResponse<TDataSource> = DataSourceResponse<TDataSource>,
23+
TData extends DataSourceData<TDataSource> = DataSourceData<TDataSource>,
24+
TError extends DataSourceError<TDataSource> = DataSourceError<TDataSource>,
25+
TOptions extends DataSourceOptions<TDataSource> = DataSourceOptions<TDataSource>,
26+
> implements DataObserver<TDataSource>
27+
{
28+
readonly context: TContext;
29+
readonly dataSource: TDataSource;
30+
readonly observer: QueryObserver<TResponse, TError, TData, TResponse>;
31+
32+
constructor(context: TContext, dataSource: TDataSource, params: TParams, options?: TOptions) {
33+
this.context = context;
34+
this.dataSource = dataSource;
35+
this.observer = new QueryObserver(
36+
context.queryClient,
37+
this.composeOptions(context, dataSource, params, options),
38+
);
39+
}
40+
41+
getCurrentState() {
42+
return this.transformResult(this.observer.getCurrentResult());
43+
}
44+
45+
updateParams(params: TParams, options?: TOptions) {
46+
this.observer.setOptions(
47+
this.composeOptions(this.context, this.dataSource, params, options),
48+
);
49+
}
50+
51+
subscribe(listener: DataListener<TDataSource>) {
52+
return this.observer.subscribe((result) => {
53+
listener(this.transformResult(result));
54+
});
55+
}
56+
57+
private composeOptions(
58+
context: TContext,
59+
dataSource: TDataSource,
60+
params: TParams,
61+
options?: TOptions,
62+
): QueryObserverOptions<TResponse, TError, TData, TResponse> {
63+
return composeOptions(context, dataSource, params, options);
64+
}
65+
66+
private transformResult(result: QueryObserverResult<TData, TError>) {
67+
return transformResult<TDataSource>(result);
68+
}
69+
}

src/react/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
export {useData} from './useData';
2+
13
export {DataManagerContext, useDataManager} from './DataManagerContext';
24

35
export type {WithDataManagerProps} from './withDataManager';

src/react/useData.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import {useEffect, useState} from 'react';
2+
3+
import type {
4+
AnyDataSource,
5+
DataObserver,
6+
DataSourceContext,
7+
DataSourceOptions,
8+
DataSourceParams,
9+
DataSourceState,
10+
} from '../core';
11+
import {composeKey} from '../core';
12+
13+
export const useData = <TDataSource extends AnyDataSource>(
14+
dataSource: TDataSource,
15+
context: DataSourceContext<TDataSource>,
16+
params: DataSourceParams<TDataSource>,
17+
options?: DataSourceOptions<TDataSource>,
18+
): [DataSourceState<TDataSource>, DataObserver<TDataSource>] => {
19+
const [observer] = useState(() => dataSource.observe(context, params, options));
20+
const [state, setState] = useState<DataSourceState<TDataSource>>(() =>
21+
observer.getCurrentState(),
22+
);
23+
24+
useEffect(() => {
25+
return observer.subscribe(setState);
26+
}, [observer]);
27+
28+
const key = composeKey(dataSource, params);
29+
30+
useEffect(() => {
31+
observer.updateParams(params, options);
32+
// Key replaces params and other deps are static
33+
// eslint-disable-next-line react-hooks/exhaustive-deps
34+
}, [options, key]);
35+
36+
return [state, observer];
37+
};

0 commit comments

Comments
 (0)