diff --git a/apps/trading/pages/_app.page.tsx b/apps/trading/pages/_app.page.tsx index 1eeea5019..f884b6324 100644 --- a/apps/trading/pages/_app.page.tsx +++ b/apps/trading/pages/_app.page.tsx @@ -33,6 +33,8 @@ import { ViewingBanner } from '../components/viewing-banner'; import { Banner } from '../components/banner'; import { AppLoader, DynamicLoader } from '../components/app-loader'; import { Navbar } from '../components/navbar'; +import { useDataProvider } from '@vegaprotocol/react-helpers'; +import { activeOrdersProvider } from '@vegaprotocol/orders'; const DEFAULT_TITLE = t('Welcome to Vega trading!'); @@ -95,6 +97,7 @@ function AppBody({ Component }: AppProps) { + ); } @@ -127,6 +130,18 @@ function VegaTradingApp(props: AppProps) { export default VegaTradingApp; +const PartyData = () => { + const { pubKey } = useVegaWallet(); + const variables = { partyId: pubKey || '' }; + const skip = !pubKey; + useDataProvider({ + dataProvider: activeOrdersProvider, + variables, + skip, + }); + return null; +}; + const MaybeConnectEagerly = () => { useVegaEagerConnect(Connectors); useEthereumEagerConnect(); diff --git a/libs/datagrid/src/lib/filters/date-range-filter.spec.tsx b/libs/datagrid/src/lib/filters/date-range-filter.spec.tsx index c5235e730..e5cebecd3 100644 --- a/libs/datagrid/src/lib/filters/date-range-filter.spec.tsx +++ b/libs/datagrid/src/lib/filters/date-range-filter.spec.tsx @@ -8,7 +8,7 @@ const commonProps = { describe('DateRangeFilter', () => { it('should be properly rendered', async () => { - const defaultRangeFilter = { + const defaultValue = { start: '2023-02-14T13:53:01+01:00', end: '2023-02-21T13:53:01+01:00', }; @@ -17,7 +17,7 @@ describe('DateRangeFilter', () => { render( ); diff --git a/libs/datagrid/src/lib/filters/date-range-filter.tsx b/libs/datagrid/src/lib/filters/date-range-filter.tsx index fb1b96c67..a2e8d8041 100644 --- a/libs/datagrid/src/lib/filters/date-range-filter.tsx +++ b/libs/datagrid/src/lib/filters/date-range-filter.tsx @@ -1,5 +1,5 @@ import type { ChangeEvent } from 'react'; -import { useEffect, useMemo } from 'react'; +import { useEffect, useMemo, useRef } from 'react'; import type * as Schema from '@vegaprotocol/types'; import { forwardRef, useImperativeHandle, useState } from 'react'; import type { IDoesFilterPassParams, IFilterParams } from 'ag-grid-community'; @@ -17,9 +17,9 @@ import { formatForInput } from '@vegaprotocol/utils'; import { t } from '@vegaprotocol/i18n'; import { InputError } from '@vegaprotocol/ui-toolkit'; -const defaultFilterValue: Schema.DateRange = {}; +const defaultValue: Schema.DateRange = {}; export interface DateRangeFilterProps extends IFilterParams { - defaultRangeFilter?: Schema.DateRange; + defaultValue?: Schema.DateRange; maxSubDays?: number; maxNextDays?: number; maxDaysRange?: number; @@ -27,8 +27,9 @@ export interface DateRangeFilterProps extends IFilterParams { export const DateRangeFilter = forwardRef( (props: DateRangeFilterProps, ref) => { - const defaultDates = props?.defaultRangeFilter || defaultFilterValue; + const defaultDates = props?.defaultValue || defaultValue; const [value, setValue] = useState(defaultDates); + const valueRef = useRef(value); const [error, setError] = useState(''); const [minStartDate, maxStartDate, minEndDate, maxEndDate] = useMemo(() => { const minStartDate = @@ -93,7 +94,7 @@ export const DateRangeFilter = forwardRef( }, isFilterActive() { - return value.start || value.end; + return valueRef.current.start || valueRef.current.end; }, getModel() { @@ -101,13 +102,13 @@ export const DateRangeFilter = forwardRef( return null; } - return { value }; + return { value: valueRef.current }; }, setModel(model?: { value: Schema.DateRange } | null) { - setValue( - model?.value || props?.defaultRangeFilter || defaultFilterValue - ); + valueRef.current = + model?.value || props?.defaultValue || defaultValue; + setValue(valueRef.current); }, }; }); @@ -185,10 +186,8 @@ export const DateRangeFilter = forwardRef( update = { ...update, end: checkForEndDate(endDate, startDate) }; if (validate(name, date, update)) { - setValue((curr) => ({ - ...curr, - ...update, - })); + valueRef.current = { ...valueRef.current, ...update }; + setValue(valueRef.current); } }; useEffect(() => { @@ -241,7 +240,8 @@ export const DateRangeFilter = forwardRef( className="ag-standard-button ag-filter-apply-panel-button" onClick={() => { setError(''); - setValue(defaultDates); + valueRef.current = defaultDates; + setValue(valueRef.current); }} > {t('Reset')} diff --git a/libs/datagrid/src/lib/filters/set-filter.tsx b/libs/datagrid/src/lib/filters/set-filter.tsx index b73e14e17..c2363d625 100644 --- a/libs/datagrid/src/lib/filters/set-filter.tsx +++ b/libs/datagrid/src/lib/filters/set-filter.tsx @@ -1,10 +1,17 @@ import type { ChangeEvent } from 'react'; -import { forwardRef, useEffect, useImperativeHandle, useState } from 'react'; +import { + forwardRef, + useEffect, + useImperativeHandle, + useState, + useRef, +} from 'react'; import type { IDoesFilterPassParams, IFilterParams } from 'ag-grid-community'; import { t } from '@vegaprotocol/i18n'; export const SetFilter = forwardRef((props: IFilterParams, ref) => { const [value, setValue] = useState([]); + const valueRef = useRef(value); // expose AG Grid Filter Lifecycle callbacks useImperativeHandle(ref, () => { @@ -28,29 +35,28 @@ export const SetFilter = forwardRef((props: IFilterParams, ref) => { }, isFilterActive() { - return value.length !== 0; + return valueRef.current.length !== 0; }, getModel() { if (!this.isFilterActive()) { return null; } - - return { value }; + return { value: valueRef.current }; }, setModel(model?: { value: string[] } | null) { - setValue(!model ? [] : model.value); + valueRef.current = !model ? [] : model.value; + setValue(valueRef.current); }, }; }); const onChange = (event: ChangeEvent) => { - setValue( - event.target.checked - ? [...value, event.target.value] - : value.filter((v) => v !== event.target.value) - ); + valueRef.current = event.target.checked + ? [...value, event.target.value] + : value.filter((v) => v !== event.target.value); + setValue(valueRef.current); }; useEffect(() => { @@ -77,7 +83,7 @@ export const SetFilter = forwardRef((props: IFilterParams, ref) => { diff --git a/libs/deal-ticket/src/hooks/use-initial-margin.ts b/libs/deal-ticket/src/hooks/use-initial-margin.ts index e7dec56af..1db1d2e25 100644 --- a/libs/deal-ticket/src/hooks/use-initial-margin.ts +++ b/libs/deal-ticket/src/hooks/use-initial-margin.ts @@ -15,8 +15,8 @@ export const useInitialMargin = ( marketId: OrderSubmissionBody['orderSubmission']['marketId'], order?: OrderSubmissionBody['orderSubmission'] ) => { - const { pubKey: partyId } = useVegaWallet(); - const commonVariables = { marketId, partyId: partyId || '' }; + const { pubKey } = useVegaWallet(); + const commonVariables = { marketId, partyId: pubKey || '' }; const { data: marketData } = useDataProvider({ dataProvider: marketDataProvider, variables: { marketId }, @@ -24,7 +24,7 @@ export const useInitialMargin = ( const { data: activeVolumeAndMargin } = useDataProvider({ dataProvider: volumeAndMarginProvider, variables: commonVariables, - skip: !partyId, + skip: !pubKey, }); const { data: marketInfo } = useDataProvider({ dataProvider: marketInfoProvider, diff --git a/libs/ledger/src/lib/ledger-table.tsx b/libs/ledger/src/lib/ledger-table.tsx index 679f4dcbe..d2b4e1925 100644 --- a/libs/ledger/src/lib/ledger-table.tsx +++ b/libs/ledger/src/lib/ledger-table.tsx @@ -38,10 +38,10 @@ export const TransferTooltipCellComponent = ({ ); }; -const defaultRangeFilter = { start: formatRFC3339(subDays(Date.now(), 7)) }; +const defaultValue = { start: formatRFC3339(subDays(Date.now(), 7)) }; const dateRangeFilterParams = { maxNextDays: 0, - defaultRangeFilter, + defaultValue, }; type LedgerEntryProps = TypedDataAgGrid; diff --git a/libs/orders/src/lib/components/order-data-provider/order-data-provider.ts b/libs/orders/src/lib/components/order-data-provider/order-data-provider.ts index 833769829..cd66c8f06 100644 --- a/libs/orders/src/lib/components/order-data-provider/order-data-provider.ts +++ b/libs/orders/src/lib/components/order-data-provider/order-data-provider.ts @@ -150,7 +150,7 @@ export const update = ( }); }; -export const ordersProvider = makeDataProvider< +const ordersProvider = makeDataProvider< OrdersQuery, ReturnType, OrdersUpdateSubscription, @@ -165,11 +165,36 @@ export const ordersProvider = makeDataProvider< pagination: { getPageInfo, append, - first: 100, + first: 1000, }, additionalContext: { isEnlargedTimeout: true }, }); +export const activeOrdersProvider = makeDerivedDataProvider< + ReturnType, + never, + { partyId: string; marketId?: string } +>( + [ + (callback, client, variables) => + ordersProvider(callback, client, { + partyId: variables.partyId, + filter: { + status: [OrderStatus.STATUS_ACTIVE, OrderStatus.STATUS_PARKED], + }, + }), + ], + (partsData, variables, prevData, parts, subscriptions) => { + if (!parts[0].isUpdate && subscriptions && subscriptions[0].load) { + subscriptions[0].load(); + } + const orders = partsData[0] as ReturnType; + return variables.marketId + ? orders.filter((edge) => variables.marketId === edge.node.market.id) + : orders; + } +); + export const ordersWithMarketProvider = makeDerivedDataProvider< (OrderEdge | null)[], Order[], @@ -193,63 +218,20 @@ export const ordersWithMarketProvider = makeDerivedDataProvider< combineInsertionData ); -const hasActiveOrderProviderInternal = makeDataProvider< - OrdersQuery, - boolean, - OrdersUpdateSubscription, - ReturnType, - OrdersQueryVariables ->({ - query: OrdersDocument, - subscriptionQuery: OrdersUpdateDocument, - update: ( - data: boolean | null, - delta: ReturnType, - reload: () => void - ) => { - const orders = delta?.filter( - (order) => !(order.peggedOrder || order.liquidityProvisionId) - ); - if (!orders?.length) { - return data; - } - const hasActiveOrders = orders.some( - (order) => order.status === OrderStatus.STATUS_ACTIVE - ); - if (hasActiveOrders) { - return true; - } else if (data && !hasActiveOrders) { - reload(); - } - return data; - }, - getData: (responseData: OrdersQuery | null) => { - const hasActiveOrder = !!responseData?.party?.ordersConnection?.edges?.some( - (order) => !(order.node.peggedOrder || order.node.liquidityProvision) - ); - return hasActiveOrder; - }, - getDelta, -}); - export const hasActiveOrderProvider = makeDerivedDataProvider< boolean, never, { partyId: string; marketId?: string } ->( - [ - (callback, client, { partyId, marketId }) => - hasActiveOrderProviderInternal(callback, client, { - marketIds: marketId ? [marketId] : undefined, - filter: { - status: [OrderStatus.STATUS_ACTIVE], - excludeLiquidity: true, - }, - pagination: { - first: 1, - }, - partyId, - } as OrdersQueryVariables), - ], - (parts) => parts[0] -); +>([activeOrdersProvider], (parts) => !!parts[0].length); + +export const hasAmendableOrderProvider = makeDerivedDataProvider< + boolean, + never, + { partyId: string; marketId?: string } +>([activeOrdersProvider], (parts) => { + const activeOrders = parts[0] as ReturnType; + const hasAmendableOrder = activeOrders.some( + (edge) => !(edge.node.liquidityProvision || edge.node.peggedOrder) + ); + return hasAmendableOrder; +}); diff --git a/libs/orders/src/lib/components/order-list-manager/order-list-manager.tsx b/libs/orders/src/lib/components/order-list-manager/order-list-manager.tsx index 4be809922..f662a2b07 100644 --- a/libs/orders/src/lib/components/order-list-manager/order-list-manager.tsx +++ b/libs/orders/src/lib/components/order-list-manager/order-list-manager.tsx @@ -8,7 +8,7 @@ import type { GridReadyEvent } from 'ag-grid-community'; import { OrderListTable } from '../order-list/order-list'; import { useOrderListData } from './use-order-list-data'; -import { useHasActiveOrder } from '../../order-hooks/use-has-active-order'; +import { useHasAmendableOrder } from '../../order-hooks/use-has-amendable-order'; import type { Filter, Sort } from './use-order-list-data'; import { useBottomPlaceholder } from '@vegaprotocol/react-helpers'; import { OrderStatus } from '@vegaprotocol/types'; @@ -16,6 +16,7 @@ import { normalizeOrderAmendment, useVegaTransactionStore, } from '@vegaprotocol/wallet'; +import isEqual from 'lodash/isEqual'; import type { OrderTxUpdateFieldsFragment } from '@vegaprotocol/wallet'; import { OrderEditDialog } from '../order-list/order-edit-dialog'; import type { Order, OrderEdge } from '../order-data-provider'; @@ -28,27 +29,18 @@ export interface OrderListManagerProps { enforceBottomPlaceholder?: boolean; } -const CancelAllOrdersButton = ({ - onClick, - marketId, -}: { - onClick: (marketId?: string) => void; - marketId?: string; -}) => { - const hasActiveOrder = useHasActiveOrder(marketId); - return hasActiveOrder ? ( -
- -
- ) : null; -}; +const CancelAllOrdersButton = ({ onClick }: { onClick: () => void }) => ( +
+ +
+); const initialFilter: Filter = { status: { @@ -68,9 +60,10 @@ export const OrderListManager = ({ const scrolledToTop = useRef(false); const [sort, setSort] = useState(); const [filter, setFilter] = useState(initialFilter); + const filterRef = useRef(initialFilter); const [editOrder, setEditOrder] = useState(null); const create = useVegaTransactionStore((state) => state.create); - const hasActiveOrder = useHasActiveOrder(marketId); + const hasAmendableOrder = useHasAmendableOrder(marketId); const { data, error, loading, reload } = useOrderListData({ partyId, @@ -86,12 +79,16 @@ export const OrderListManager = ({ ...bottomPlaceholderProps } = useBottomPlaceholder({ gridRef, - disabled: !enforceBottomPlaceholder && !isReadOnly && !hasActiveOrder, + disabled: !enforceBottomPlaceholder && !isReadOnly && !hasAmendableOrder, }); const onFilterChanged = useCallback( (event: FilterChangedEvent) => { const updatedFilter = event.api.getFilterModel(); + if (isEqual(updatedFilter, filterRef.current)) { + return; + } + filterRef.current = updatedFilter; if (Object.keys(updatedFilter).length) { setFilter(updatedFilter); } else { @@ -142,16 +139,13 @@ export const OrderListManager = ({ setDataCount(gridRef.current?.api?.getModel().getRowCount() ?? 0); }, [data]); - const cancelAll = useCallback( - (marketId?: string) => { - create({ - orderCancellation: { - marketId, - }, - }); - }, - [create] - ); + const cancelAll = useCallback(() => { + create({ + orderCancellation: { + marketId, + }, + }); + }, [create, marketId]); const extractedData = data && !loading ? data @@ -188,8 +182,8 @@ export const OrderListManager = ({ /> - {!isReadOnly && ( - + {!isReadOnly && hasAmendableOrder && ( + )} {editOrder && ( { +export const useHasAmendableOrder = (marketId?: string) => { const { pubKey } = useVegaWallet(); - const [hasActiveOrder, setHasActiveOrder] = useState(false); + const [hasAmendableOrder, setHasAmendableOrder] = useState(false); const update = useCallback(({ data }: { data: boolean | null }) => { - setHasActiveOrder(Boolean(data)); + setHasAmendableOrder(Boolean(data)); return true; }, []); useDataProvider({ - dataProvider: hasActiveOrderProvider, + dataProvider: hasAmendableOrderProvider, update, variables: { partyId: pubKey || '', @@ -20,5 +20,5 @@ export const useHasActiveOrder = (marketId?: string) => { skip: !pubKey, }); - return hasActiveOrder; + return hasAmendableOrder; }; diff --git a/libs/orders/src/lib/order-hooks/use-pending-orders-volume.ts b/libs/orders/src/lib/order-hooks/use-pending-orders-volume.ts deleted file mode 100644 index 988570419..000000000 --- a/libs/orders/src/lib/order-hooks/use-pending-orders-volume.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { useState, useCallback } from 'react'; -import { OrderStatus, Side } from '@vegaprotocol/types'; -import { ordersProvider } from '../components/order-data-provider/order-data-provider'; -import type { OrderFieldsFragment } from '../components/order-data-provider/__generated__/Orders'; -import type { Edge } from '@vegaprotocol/utils'; -import { useDataProvider } from '@vegaprotocol/react-helpers'; - -const sumVolume = (orders: (Edge | null)[], side: Side) => - orders - .reduce( - (sum, order) => - order?.node.side === side - ? sum + - BigInt( - order?.node.status === OrderStatus.STATUS_PARTIALLY_FILLED - ? order?.node.remaining - : order?.node.size - ) - : sum, - BigInt(0) - ) - .toString(); - -export const useActiveOrdersVolumeAndMargin = ( - partyId: string | null | undefined, - marketId: string -) => { - const [buyVolume, setBuyVolume] = useState(); - const [sellVolume, setSellVolume] = useState(); - const [buyInitialMargin, setBuyInitialMargin] = useState< - string | undefined - >(); - const [sellInitialMargin, setSellInitialMargin] = useState< - string | undefined - >(); - const update = useCallback( - ({ data }: { data: (Edge | null)[] | null }) => { - if (!data) { - setBuyVolume(undefined); - setSellVolume(undefined); - setBuyInitialMargin(undefined); - setSellInitialMargin(undefined); - } else { - setBuyVolume(sumVolume(data, Side.SIDE_BUY)); - setSellVolume(sumVolume(data, Side.SIDE_SELL)); - } - return true; - }, - [] - ); - useDataProvider({ - dataProvider: ordersProvider, - update, - variables: { - partyId: partyId || '', - marketIds: [marketId], - filter: { - status: [ - OrderStatus.STATUS_ACTIVE, - OrderStatus.STATUS_PARTIALLY_FILLED, - ], - }, - }, - skip: !partyId, - }); - return buyVolume || sellVolume - ? { - buyVolume, - sellVolume, - buyInitialMargin, - sellInitialMargin, - } - : undefined; -}; diff --git a/libs/positions/src/lib/positions-data-providers.ts b/libs/positions/src/lib/positions-data-providers.ts index 7e827927e..eead272c9 100644 --- a/libs/positions/src/lib/positions-data-providers.ts +++ b/libs/positions/src/lib/positions-data-providers.ts @@ -30,12 +30,12 @@ import { import { marginsDataProvider } from './margin-data-provider'; import { calculateMargins } from './margin-calculator'; import type { Edge } from '@vegaprotocol/utils'; -import { OrderStatus, Side } from '@vegaprotocol/types'; +import { Side } from '@vegaprotocol/types'; import { marketInfoProvider } from '@vegaprotocol/market-info'; import type { MarketInfoQuery } from '@vegaprotocol/market-info'; import { marketDataProvider } from '@vegaprotocol/market-list'; import type { MarketData } from '@vegaprotocol/market-list'; -import { ordersProvider } from '@vegaprotocol/orders'; +import { activeOrdersProvider } from '@vegaprotocol/orders'; import type { OrderFieldsFragment } from '@vegaprotocol/orders'; import type { PositionStatus } from '@vegaprotocol/types'; @@ -350,12 +350,9 @@ export const volumeAndMarginProvider = makeDerivedDataProvider< >( [ (callback, client, { partyId, marketId }) => - ordersProvider(callback, client, { + activeOrdersProvider(callback, client, { partyId, - marketIds: [marketId], - filter: { - status: [OrderStatus.STATUS_ACTIVE, OrderStatus.STATUS_PARKED], - }, + marketId, }), (callback, client, variables) => marketDataProvider(callback, client, { marketId: variables.marketId }), diff --git a/libs/react-helpers/src/hooks/use-data-provider.ts b/libs/react-helpers/src/hooks/use-data-provider.ts index addd5d3fc..3419983fc 100644 --- a/libs/react-helpers/src/hooks/use-data-provider.ts +++ b/libs/react-helpers/src/hooks/use-data-provider.ts @@ -1,10 +1,10 @@ -import { useState, useEffect, useRef, useCallback } from 'react'; +import { useState, useEffect, useRef, useMemo, useCallback } from 'react'; import throttle from 'lodash/throttle'; -import isEqual from 'lodash/isEqual'; +import isEqualWith from 'lodash/isEqualWith'; import { useApolloClient } from '@apollo/client'; -import { usePrevious } from './use-previous'; import type { OperationVariables } from '@apollo/client'; import type { Subscribe, Load, UpdateCallback } from '@vegaprotocol/utils'; +import { variablesIsEqualCustomizer } from '@vegaprotocol/utils'; export interface useDataProviderParams< Data, @@ -62,13 +62,19 @@ export const useDataProvider = < const flushRef = useRef<(() => void) | undefined>(undefined); const reloadRef = useRef<((force?: boolean) => void) | undefined>(undefined); const loadRef = useRef | undefined>(undefined); - const prevVariables = usePrevious(props.variables); - const [variables, setVariables] = useState(props.variables); - useEffect(() => { - if (!isEqual(prevVariables, props.variables)) { - setVariables(props.variables); + const variablesRef = useRef(props.variables); + const variables = useMemo(() => { + if ( + !isEqualWith( + variablesRef.current, + props.variables, + variablesIsEqualCustomizer + ) + ) { + variablesRef.current = props.variables; } - }, [props.variables, prevVariables]); + return variablesRef.current; + }, [props.variables]); const flush = useCallback(() => { if (flushRef.current) { flushRef.current(); diff --git a/libs/utils/src/lib/generic-data-provider.spec.ts b/libs/utils/src/lib/generic-data-provider.spec.ts index e68d588f0..c48d685e0 100644 --- a/libs/utils/src/lib/generic-data-provider.spec.ts +++ b/libs/utils/src/lib/generic-data-provider.spec.ts @@ -45,7 +45,7 @@ type CombinedData = { type SubscriptionData = QueryData; type Delta = Data; -type Variables = { var: string }; +type Variables = { var: string; filter?: string[] }; const update = jest.fn< ReturnType>, @@ -231,10 +231,14 @@ describe('data provider', () => { clientSubscribeSubscribe.mockClear(); }); it('memoize instance and unsubscribe if no subscribers', () => { - const subscription1 = subscribe(jest.fn(), client, variables); - const subscription2 = subscribe(jest.fn(), client, { ...variables }); - // const subscription1 = subscribe(jest.fn(), client); - // const subscription2 = subscribe(jest.fn(), client); + const subscription1 = subscribe(jest.fn(), client, { + ...variables, + filter: ['1', '2'], + }); + const subscription2 = subscribe(jest.fn(), client, { + ...variables, + filter: ['2', '1'], + }); expect(clientSubscribeSubscribe.mock.calls.length).toEqual(1); subscription1.unsubscribe(); expect(clientSubscribeUnsubscribe.mock.calls.length).toEqual(0); diff --git a/libs/utils/src/lib/generic-data-provider.ts b/libs/utils/src/lib/generic-data-provider.ts index 2dae214a5..9de04913e 100644 --- a/libs/utils/src/lib/generic-data-provider.ts +++ b/libs/utils/src/lib/generic-data-provider.ts @@ -10,7 +10,7 @@ import type { } from '@apollo/client'; import type { GraphQLErrors } from '@apollo/client/errors'; import type { Subscription } from 'zen-observable-ts'; -import isEqual from 'lodash/isEqual'; +import isEqualWith from 'lodash/isEqualWith'; import { isNotFoundGraphQLError } from './apollo-client'; import type * as Schema from '@vegaprotocol/types'; interface UpdateData { @@ -512,11 +512,26 @@ function makeDataProviderInternal< }; } +/** + * Compares two arrays assuming that they are sets of primitive values, used to compare gql query variables + */ +export const variablesIsEqualCustomizer: NonNullable< + Parameters['2'] +> = (value, other) => { + if (Array.isArray(value) && Array.isArray(other)) { + return ( + value.length === other.length && + new Set([...value, ...other]).size === value.length + ); + } + return undefined; +}; + /** * Memoizes data provider instances using query variables as cache key * * @param fn - * @returns subscibe function + * @returns subscribe function */ const memoize = < Data, @@ -530,7 +545,9 @@ const memoize = < variables?: Variables; }[] = []; return (variables?: Variables) => { - const cached = cache.find((c) => isEqual(c.variables, variables)); + const cached = cache.find((c) => + isEqualWith(c.variables, variables, variablesIsEqualCustomizer) + ); if (cached) { return cached.subscribe; } @@ -582,8 +599,10 @@ export function makeDataProvider< const getInstance = memoize(() => makeDataProviderInternal(params) ); - return (callback, client, variables) => - getInstance(variables)(callback, client, variables); + return (callback, client, variables) => { + const instance = getInstance(variables)(callback, client, variables); + return instance; + }; } /** @@ -605,7 +624,9 @@ export type CombineDerivedData< > = ( data: DerivedPart['data'][], variables: Variables, - prevData: Data | null + prevData: Data | null, + parts: DerivedPart[], + subscriptions?: ReturnType>[] ) => Data | null; export type CombineDerivedDelta< @@ -687,7 +708,9 @@ function makeDerivedDataProviderInternal< ? combineData( parts.map((part) => part.data), variables, - data + data, + parts, + subscriptions ) : data; if (