chore: fix update results from dataProvider when variable change (#1962)

* chore: fix race conditions in useDataProvider hook

* chore: fix race conditions in useDataProvider hook - populate fix to other modules

* chore: fix race conditions in useDataProvider hook - populate fix to other modules

* chore: fix race conditions in useDataProvider hook - populate fix to other modules

* chore: fix race conditions in useDataProvider hook - populate fix to other modules

Co-authored-by: maciek <maciek@vegaprotocol.io>
This commit is contained in:
macqbat 2022-11-07 13:14:21 +01:00 committed by GitHub
parent e2b789000c
commit dc5881b71b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 102 additions and 95 deletions

View File

@ -32,10 +32,8 @@ const AccountsManager = () => {
dataProvider: aggregatedAccountsDataProvider, dataProvider: aggregatedAccountsDataProvider,
update, update,
variables, variables,
updateOnInit: true,
}); });
if (!dataRef.current && data) {
dataRef.current = data;
}
const getRows = async ({ const getRows = async ({
successCallback, successCallback,
startRow, startRow,

View File

@ -1,8 +1,9 @@
import { useState } from 'react'; import { useCallback, useState } from 'react';
import { Button } from '@vegaprotocol/ui-toolkit'; import { Button } from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/react-helpers'; import { t } from '@vegaprotocol/react-helpers';
import { WithdrawalDialogs } from '@vegaprotocol/withdraws'; import { WithdrawalDialogs } from '@vegaprotocol/withdraws';
import { Web3Container } from '@vegaprotocol/web3'; import { Web3Container } from '@vegaprotocol/web3';
import type { AssetFieldsFragment } from '@vegaprotocol/assets';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets'; import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import { Splash } from '@vegaprotocol/ui-toolkit'; import { Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet } from '@vegaprotocol/wallet';
@ -16,6 +17,23 @@ export const AccountsContainer = () => {
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore(); const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
const [assetId, setAssetId] = useState<string>(); const [assetId, setAssetId] = useState<string>();
const onClickAsset = useCallback(
(value?: string | AssetFieldsFragment) => {
value && openAssetDetailsDialog(value);
},
[openAssetDetailsDialog]
);
const onClickWithdraw = useCallback((assetId?: string) => {
setWithdrawDialog(true);
setAssetId(assetId);
}, []);
const onClickDeposit = useCallback((assetId?: string) => {
setDepositDialog(true);
setAssetId(assetId);
}, []);
if (!pubKey) { if (!pubKey) {
return ( return (
<Splash> <Splash>
@ -30,17 +48,9 @@ export const AccountsContainer = () => {
<div> <div>
<AccountManager <AccountManager
partyId={pubKey} partyId={pubKey}
onClickAsset={(value) => { onClickAsset={onClickAsset}
value && openAssetDetailsDialog(value); onClickWithdraw={onClickWithdraw}
}} onClickDeposit={onClickDeposit}
onClickWithdraw={(assetId) => {
setWithdrawDialog(true);
setAssetId(assetId);
}}
onClickDeposit={(assetId) => {
setDepositDialog(true);
setAssetId(assetId);
}}
/> />
</div> </div>
<div className="flex justify-end p-2 px-[11px]"> <div className="flex justify-end p-2 px-[11px]">

View File

@ -24,7 +24,7 @@ const Portfolio = () => {
const wrapperClasses = 'h-full max-h-full flex flex-col'; const wrapperClasses = 'h-full max-h-full flex flex-col';
return ( return (
<div className={wrapperClasses}> <div className={wrapperClasses}>
<ResizableGrid vertical={true}> <ResizableGrid vertical>
<ResizableGridPanel minSize={75}> <ResizableGridPanel minSize={75}>
<PortfolioGridChild> <PortfolioGridChild>
<Tabs> <Tabs>

View File

@ -1,12 +1,11 @@
import { act, render } from '@testing-library/react'; import { act, render } from '@testing-library/react';
import { AccountManager } from './accounts-manager'; import { AccountManager } from './accounts-manager';
import * as helpers from '@vegaprotocol/react-helpers';
const mockReload = jest.fn();
jest.mock('@vegaprotocol/react-helpers', () => ({ jest.mock('@vegaprotocol/react-helpers', () => ({
...jest.requireActual('@vegaprotocol/react-helpers'), ...jest.requireActual('@vegaprotocol/react-helpers'),
useDataProvider: jest.fn(() => ({ useDataProvider: jest.fn(() => ({
data: [], data: [],
reload: mockReload,
})), })),
})); }));
@ -15,10 +14,14 @@ describe('AccountManager', () => {
const { rerender } = render( const { rerender } = render(
<AccountManager partyId="partyOne" onClickAsset={jest.fn} /> <AccountManager partyId="partyOne" onClickAsset={jest.fn} />
); );
expect(mockReload).not.toHaveBeenCalled(); expect(
(helpers.useDataProvider as jest.Mock).mock.calls[0][0].variables.partyId
).toEqual('partyOne');
await act(() => { await act(() => {
rerender(<AccountManager partyId="partyTwo" onClickAsset={jest.fn} />); rerender(<AccountManager partyId="partyTwo" onClickAsset={jest.fn} />);
}); });
expect(mockReload).toHaveBeenCalledWith(true); expect(
(helpers.useDataProvider as jest.Mock).mock.calls[1][0].variables.partyId
).toEqual('partyTwo');
}); });
}); });

View File

@ -2,7 +2,7 @@ import type { Asset } from '@vegaprotocol/assets';
import { useDataProvider } from '@vegaprotocol/react-helpers'; import { useDataProvider } from '@vegaprotocol/react-helpers';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit'; import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import type { AgGridReact } from 'ag-grid-react'; import type { AgGridReact } from 'ag-grid-react';
import { useRef, useMemo, useCallback } from 'react'; import { useRef, useMemo, useCallback, memo } from 'react';
import type { AccountFields } from './accounts-data-provider'; import type { AccountFields } from './accounts-data-provider';
import { aggregatedAccountsDataProvider } from './accounts-data-provider'; import { aggregatedAccountsDataProvider } from './accounts-data-provider';
import type { GetRowsParams } from './accounts-table'; import type { GetRowsParams } from './accounts-table';
@ -15,64 +15,56 @@ interface AccountManagerProps {
onClickDeposit?: (assetId?: string) => void; onClickDeposit?: (assetId?: string) => void;
} }
export const AccountManager = ({ export const AccountManager = memo(
onClickAsset, ({
onClickWithdraw, onClickAsset,
onClickDeposit, onClickWithdraw,
partyId, onClickDeposit,
}: AccountManagerProps) => { partyId,
const partyIdRef = useRef<string>(partyId); }: AccountManagerProps) => {
const gridRef = useRef<AgGridReact | null>(null); const gridRef = useRef<AgGridReact | null>(null);
const dataRef = useRef<AccountFields[] | null>(null); const dataRef = useRef<AccountFields[] | null>(null);
const variables = useMemo(() => ({ partyId }), [partyId]); const variables = useMemo(() => ({ partyId }), [partyId]);
const update = useCallback( const update = useCallback(
({ data }: { data: AccountFields[] | null }) => { ({ data }: { data: AccountFields[] | null }) => {
dataRef.current = data; dataRef.current = data;
gridRef.current?.api?.refreshInfiniteCache(); gridRef.current?.api?.refreshInfiniteCache();
return true; return true;
}, },
[gridRef] [gridRef]
); );
const { data, loading, error, reload } = useDataProvider< const { data, loading, error } = useDataProvider<AccountFields[], never>({
AccountFields[], dataProvider: aggregatedAccountsDataProvider,
never update,
>({ variables,
dataProvider: aggregatedAccountsDataProvider, updateOnInit: true,
update, });
variables, const getRows = async ({
}); successCallback,
if (partyId !== partyIdRef.current) { startRow,
reload(true); endRow,
partyIdRef.current = partyId; }: GetRowsParams) => {
const rowsThisBlock = dataRef.current
? dataRef.current.slice(startRow, endRow)
: [];
const lastRow = dataRef.current?.length ?? -1;
successCallback(rowsThisBlock, lastRow);
};
return (
<AsyncRenderer data={data || []} error={error} loading={loading}>
<AccountTable
rowModelType={data?.length ? 'infinite' : 'clientSide'}
rowData={data?.length ? undefined : []}
ref={gridRef}
datasource={{ getRows }}
onClickAsset={onClickAsset}
onClickDeposit={onClickDeposit}
onClickWithdraw={onClickWithdraw}
/>
</AsyncRenderer>
);
} }
if (!dataRef.current && data) { );
dataRef.current = data;
}
const getRows = async ({
successCallback,
startRow,
endRow,
}: GetRowsParams) => {
const rowsThisBlock = dataRef.current
? dataRef.current.slice(startRow, endRow)
: [];
const lastRow = dataRef.current?.length ?? -1;
successCallback(rowsThisBlock, lastRow);
};
return (
<AsyncRenderer data={data || []} error={error} loading={loading}>
<AccountTable
rowModelType={data?.length ? 'infinite' : 'clientSide'}
rowData={data?.length ? undefined : []}
ref={gridRef}
datasource={{ getRows }}
onClickAsset={onClickAsset}
onClickDeposit={onClickDeposit}
onClickWithdraw={onClickWithdraw}
/>
</AsyncRenderer>
);
};
export default AccountManager; export default memo(AccountManager);

View File

@ -86,8 +86,9 @@ describe('useFillsList Hook', () => {
addNewRows: expect.any(Function), addNewRows: expect.any(Function),
getRows: expect.any(Function), getRows: expect.any(Function),
}); });
updateMock({ data: mockData });
expect(mockRefreshAgGridApi).not.toHaveBeenCalled(); expect(mockRefreshAgGridApi).not.toHaveBeenCalled();
updateMock({ data: {} }); updateMock({ data: mockData });
expect(mockRefreshAgGridApi).toHaveBeenCalled(); expect(mockRefreshAgGridApi).toHaveBeenCalled();
}); });
}); });

View File

@ -77,11 +77,14 @@ export const useFillsList = ({ partyId, gridRef, scrolledToTop }: Props) => {
const { data, error, loading, load, totalCount } = useDataProvider< const { data, error, loading, load, totalCount } = useDataProvider<
(TradeEdge | null)[], (TradeEdge | null)[],
Trade[] Trade[]
>({ dataProvider: fillsWithMarketProvider, update, insert, variables }); >({
if (!dataRef.current && data) { dataProvider: fillsWithMarketProvider,
totalCountRef.current = totalCount; update,
dataRef.current = data; insert,
} variables,
updateOnInit: true,
});
totalCountRef.current = totalCount;
const getRows = makeInfiniteScrollGetRows<TradeEdge>( const getRows = makeInfiniteScrollGetRows<TradeEdge>(
newRows, newRows,

View File

@ -8,7 +8,7 @@ import type { IGetRowsParams } from 'ag-grid-community';
const loadMock = jest.fn(); const loadMock = jest.fn();
let mockData = null; let mockData: Edge<OrderFieldsFragment>[] | null = null;
let mockDataProviderData = { let mockDataProviderData = {
data: mockData as (Edge<OrderFieldsFragment> | null)[] | null, data: mockData as (Edge<OrderFieldsFragment> | null)[] | null,
error: undefined, error: undefined,
@ -94,8 +94,9 @@ describe('useOrderListData Hook', () => {
addNewRows: expect.any(Function), addNewRows: expect.any(Function),
getRows: expect.any(Function), getRows: expect.any(Function),
}); });
updateMock({ data: mockData, delta: [] });
expect(mockRefreshAgGridApi).not.toHaveBeenCalled(); expect(mockRefreshAgGridApi).not.toHaveBeenCalled();
updateMock({ data: [], delta: [] }); updateMock({ data: mockData, delta: [] });
expect(mockRefreshAgGridApi).toHaveBeenCalled(); expect(mockRefreshAgGridApi).toHaveBeenCalled();
}); });
@ -148,6 +149,10 @@ describe('useOrderListData Hook', () => {
endRow: 4, endRow: 4,
} as unknown as IGetRowsParams; } as unknown as IGetRowsParams;
await waitFor(async () => {
updateMock({ data: mockData });
});
await waitFor(async () => { await waitFor(async () => {
const promise = result.current.getRows(getRowsParams); const promise = result.current.getRows(getRowsParams);
updateMock({ data: mockNextData, delta: mockDelta }); updateMock({ data: mockNextData, delta: mockDelta });

View File

@ -77,11 +77,9 @@ export const useOrderListData = ({
update, update,
insert, insert,
variables, variables,
updateOnInit: true,
}); });
if (!dataRef.current && data) { totalCountRef.current = totalCount;
totalCountRef.current = totalCount;
dataRef.current = data;
}
const getRows = makeInfiniteScrollGetRows<OrderEdge>( const getRows = makeInfiniteScrollGetRows<OrderEdge>(
newRows, newRows,

View File

@ -85,12 +85,9 @@ export const TradesContainer = ({ marketId }: TradesContainerProps) => {
update, update,
insert, insert,
variables, variables,
updateOnInit: true,
}); });
totalCountRef.current = totalCount; totalCountRef.current = totalCount;
if (!dataRef.current && data) {
dataRef.current = data;
}
const getRows = makeInfiniteScrollGetRows<TradeEdge>( const getRows = makeInfiniteScrollGetRows<TradeEdge>(
newRows, newRows,
dataRef, dataRef,