vega-frontend-monorepo/libs/react-helpers/src/hooks/use-data-provider.ts
Bartłomiej Głownia b9aef78447
Add pagination support to generic-data-provider (#691)
* feat(#638): add pagination support to data-provider

* feat(#638): use infinite rowModelType in market list table

* chore(#638): code style fixes

* feat(#638): fix data provider post update callbacks, handle market list table empty data

* feat(#638): amend variable names to improve code readability
2022-07-05 15:33:50 +02:00

96 lines
3.1 KiB
TypeScript

import { useState, useEffect, useRef, useCallback } from 'react';
import { useApolloClient } from '@apollo/client';
import type { OperationVariables } from '@apollo/client';
import type { Subscribe, Pagination, Load } from '../lib/generic-data-provider';
/**
*
* @param dataProvider subscribe function created by makeDataProvider
* @param update optional function called on each delta received in subscription, if returns true updated data will be not passed from hook (component handles updates internally)
* @param variables optional
* @returns state: data, loading, error, methods: flush (pass updated data to update function without delta), restart: () => void}};
*/
export function useDataProvider<Data, Delta>({
dataProvider,
update,
insert,
variables,
}: {
dataProvider: Subscribe<Data, Delta>;
update?: ({ delta, data }: { delta: Delta; data: Data }) => boolean;
insert?: ({
insertionData,
data,
totalCount,
}: {
insertionData: Data;
data: Data;
totalCount?: number;
}) => boolean;
variables?: OperationVariables;
}) {
const client = useApolloClient();
const [data, setData] = useState<Data | null>(null);
const [totalCount, setTotalCount] = useState<number>();
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<Error | undefined>(undefined);
const flushRef = useRef<(() => void) | undefined>(undefined);
const reloadRef = useRef<((force?: boolean) => void) | undefined>(undefined);
const loadRef = useRef<Load<Data> | undefined>(undefined);
const initialized = useRef<boolean>(false);
const flush = useCallback(() => {
if (flushRef.current) {
flushRef.current();
}
}, []);
const reload = useCallback((force = false) => {
if (reloadRef.current) {
reloadRef.current(force);
}
}, []);
const load = useCallback((pagination: Pagination) => {
if (loadRef.current) {
return loadRef.current(pagination);
}
return Promise.reject();
}, []);
const callback = useCallback(
({ data, error, loading, delta, insertionData, totalCount }) => {
setError(error);
setLoading(loading);
if (!error && !loading) {
// if update or insert function returns true it means that component handles updates
// component can use flush() which will call callback without delta and cause data state update
if (initialized.current) {
if (delta && update && update({ delta, data })) {
return;
}
if (
insertionData &&
insert &&
insert({ insertionData, data, totalCount })
) {
return;
}
}
initialized.current = true;
setTotalCount(totalCount);
setData(data);
}
},
[update, insert]
);
useEffect(() => {
const { unsubscribe, flush, reload, load } = dataProvider(
callback,
client,
variables
);
flushRef.current = flush;
reloadRef.current = reload;
loadRef.current = load;
return unsubscribe;
}, [client, initialized, dataProvider, callback, variables]);
return { data, loading, error, flush, reload, load, totalCount };
}