Skip to content

Commit

Permalink
fix: reduce re-rendering caused by useQueries combine recalculations
Browse files Browse the repository at this point in the history
  • Loading branch information
dib542 committed Feb 8, 2024
1 parent ada45f2 commit ea24717
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 32 deletions.
52 changes: 31 additions & 21 deletions src/lib/web3/hooks/useDenomClients.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import useSWRImmutable from 'swr/immutable';
import { useQueries } from '@tanstack/react-query';
import { useMemo } from 'react';
import { useMemo, useRef } from 'react';
import { useDeepCompareMemoize } from 'use-deep-compare-effect';
import {
ChainRegistryClient,
Expand All @@ -13,7 +13,7 @@ import { AdditionalMintageTrace, Asset, Chain } from '@chain-registry/types';
import { useDenomTrace, useDenomTraceByDenom } from './useDenomsFromChain';
import { Token } from '../utils/tokens';
import { getAssetClient } from './useDenomsFromRegistry';
import { SWRCommon, useSwrResponse } from './useSWR';
import { SWRCommon, isEqualMap, useSwrResponse } from './useSWR';

const { REACT_APP__CHAIN_NAME = '' } = import.meta.env;

Expand Down Expand Up @@ -47,6 +47,7 @@ function useAssetClientByDenom(
const swr1 = useDenomTraceByDenom(uniqueDenoms);
const { data: denomTraceByDenom } = swr1;

const memoizedData = useRef<AssetClientByDenom>();
const { data: clientByDenom, ...swr2 } = useQueries({
queries: uniqueDenoms.flatMap((denom) => {
const trace = denomTraceByDenom?.get(denom);
Expand All @@ -64,28 +65,37 @@ function useAssetClientByDenom(
};
}),
combine: (results) => {
// compute data
const data = results.reduce<AssetClientByDenom>(
(map, { isPending, data: [denom, client] = [] }) => {
// if resolved then add data
if (!isPending && denom) {
const chainUtil = client?.getChainUtil(REACT_APP__CHAIN_NAME);
const asset = chainUtil?.getAssetByDenom(denom);
// if the client if found, return that
if (client && asset) {
return map.set(denom, client);
}
// if the client is undefined (pending) or null (not found/correct)
else {
return map.set(denom, client ? null : client);
}
}
return map;
},
new Map()
);

// update the memoized reference if the new data is different
if (!isEqualMap(data, memoizedData.current)) {
memoizedData.current = data;
}

// return memoized data and combined result state
return {
data: memoizedData.current,
isLoading: results.every((result) => result.isPending),
isValidating: results.some((result) => result.isFetching),
data: results.reduce<AssetClientByDenom>(
(map, { isPending, data: [denom, client] = [] }) => {
// if resolved then add data
if (!isPending && denom) {
const chainUtil = client?.getChainUtil(REACT_APP__CHAIN_NAME);
const asset = chainUtil?.getAssetByDenom(denom);
// if the client if found, return that
if (client && asset) {
return map.set(denom, client);
}
// if the client is undefined (pending) or null (not found/correct)
else {
return map.set(denom, client ? null : client);
}
}
return map;
},
new Map()
),
error: results.find((result) => result.error)?.error,
};
},
Expand Down
33 changes: 22 additions & 11 deletions src/lib/web3/hooks/useDenomsFromChain.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { useQueries } from '@tanstack/react-query';
import { useDeepCompareMemoize } from 'use-deep-compare-effect';
import { useRef } from 'react';
import { DenomTrace } from '@duality-labs/neutronjs/types/codegen/ibc/applications/transfer/v1/transfer';

import { useIbcRestClient } from '../clients/restClients';
import { useDefaultDenomTraceByDenom } from './useDenomsFromRegistry';
import { SWRCommon, useSwrResponse } from './useSWR';
import { SWRCommon, isEqualMap, useSwrResponse } from './useSWR';

type DenomTraceByDenom = Map<string, DenomTrace>;

Expand All @@ -28,6 +29,7 @@ export function useDenomTraceByDenom(

const restClient = useIbcRestClient();

const memoizedData = useRef<DenomTraceByDenom>();
const { data: denomTraceByDenom, ...swr } = useQueries({
queries: ibcDenoms.flatMap((denom) => {
const hash = denom.split('ibc/').at(1);
Expand Down Expand Up @@ -62,19 +64,28 @@ export function useDenomTraceByDenom(
return [];
}),
combine: (results) => {
// compute data
const data = results.reduce<DenomTraceByDenom>(
(map, { data: [denom, trace] = [] }) => {
// if resolved then add data
if (denom && trace) {
return map.set(denom, trace);
}
return map;
},
new Map()
);

// update the memoized reference if the new data is different
if (!isEqualMap(data, memoizedData.current)) {
memoizedData.current = data;
}

// return memoized data and combined result state
return {
data: memoizedData.current,
isLoading: results.every((result) => result.isPending),
isValidating: results.some((result) => result.isFetching),
data: results.reduce<DenomTraceByDenom>(
(map, { data: [denom, trace] = [] }) => {
// if resolved then add data
if (denom && trace) {
return map.set(denom, trace);
}
return map;
},
new Map()
),
error: results.find((result) => result.error)?.error,
};
},
Expand Down
25 changes: 25 additions & 0 deletions src/lib/web3/hooks/useSWR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,28 @@ export function useSwrResponseFromReactQuery<T>(
);
return useSwrResponse(data, swr1, swr2);
}

export function isEqualMap<K, V>(
map1: Map<K, V>,
map2: Map<K, V> = new Map<K, V>()
) {
// compare map keys and values if they are the same size
if (map1.size === map2.size) {
const entries1 = map1.entries();
const entries2 = map2.entries();
for (let i = 0; i < map1.size; i++) {
const [key1, value1] = entries1.next().value;
const [key2, value2] = entries2.next().value;
if (key1 !== key2 || value1 !== value2) {
// an item is different
return false;
}
}
// no changes found
return true;
}
// the map size is different
else {
return false;
}
}

0 comments on commit ea24717

Please sign in to comment.