From f75da1892a3f5cc81a79fda11df144605c218e40 Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Mon, 3 Jun 2024 22:43:42 -0400 Subject: [PATCH] converted more selectors to queries --- .../stateful/command/contexts/generic/dao.tsx | 36 +++++----- .../components/dao/tabs/SubDaosTab.tsx | 24 ++++--- .../stateful/hooks/useCachedLoadingQuery.ts | 20 ++---- packages/stateful/queries/dao.ts | 68 +++++++++++++++++-- packages/stateful/recoil/selectors/dao.ts | 52 -------------- 5 files changed, 99 insertions(+), 101 deletions(-) diff --git a/packages/stateful/command/contexts/generic/dao.tsx b/packages/stateful/command/contexts/generic/dao.tsx index 032b511f6..2cb3e8ea1 100644 --- a/packages/stateful/command/contexts/generic/dao.tsx +++ b/packages/stateful/command/contexts/generic/dao.tsx @@ -5,17 +5,14 @@ import { HomeOutlined, InboxOutlined, } from '@mui/icons-material' +import { useQueryClient } from '@tanstack/react-query' import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { useRecoilState } from 'recoil' import useDeepCompareEffect from 'use-deep-compare-effect' import { navigatingToHrefAtom } from '@dao-dao/state' -import { - useCachedLoading, - useDaoInfoContext, - useDaoNavHelpers, -} from '@dao-dao/stateless' +import { useDaoInfoContext, useDaoNavHelpers } from '@dao-dao/stateless' import { ContractVersion, Feature } from '@dao-dao/types' import { CommandModalContextMaker, @@ -26,8 +23,13 @@ import { import { getDisplayNameForChainId, getFallbackImage } from '@dao-dao/utils' import { DaoProvidersWithoutInfo } from '../../../components' -import { useDaoTabs, useFollowingDaos, useGovDaoTabs } from '../../../hooks' -import { chainSubDaoInfosSelector, subDaoInfosSelector } from '../../../recoil' +import { + useCachedLoadingQuery, + useDaoTabs, + useFollowingDaos, + useGovDaoTabs, +} from '../../../hooks' +import { daoQueries } from '../../../queries' export const makeGenericDaoContext: CommandModalContextMaker<{ dao: CommandModalDaoInfo @@ -68,19 +70,19 @@ export const makeGenericDaoContext: CommandModalContextMaker<{ const daoPageHref = getDaoPath(coreAddress) const createProposalHref = getDaoProposalPath(coreAddress, 'create') - const subDaosLoading = useCachedLoading( + const queryClient = useQueryClient() + const subDaosLoading = useCachedLoadingQuery( coreVersion === ContractVersion.Gov - ? chainSubDaoInfosSelector({ - chainId, - }) - : supportedFeatures[Feature.SubDaos] - ? subDaoInfosSelector({ + ? daoQueries.chainSubDaoInfos(queryClient, { chainId, - coreAddress, }) - : // Passing undefined here returns an infinite loading state, which is - // fine because it's never used. - undefined, + : { + ...daoQueries.subDaoInfos(queryClient, { + chainId, + coreAddress, + }), + enabled: !!supportedFeatures[Feature.SubDaos], + }, [] ) diff --git a/packages/stateful/components/dao/tabs/SubDaosTab.tsx b/packages/stateful/components/dao/tabs/SubDaosTab.tsx index 3bc53c36a..b8e57bdfe 100644 --- a/packages/stateful/components/dao/tabs/SubDaosTab.tsx +++ b/packages/stateful/components/dao/tabs/SubDaosTab.tsx @@ -1,7 +1,7 @@ +import { useQueryClient } from '@tanstack/react-query' + import { SubDaosTab as StatelessSubDaosTab, - useCachedLoading, - useChain, useDaoInfoContext, useDaoNavHelpers, } from '@dao-dao/stateless' @@ -9,24 +9,26 @@ import { ActionKey, Feature } from '@dao-dao/types' import { getDaoProposalSinglePrefill } from '@dao-dao/utils' import { useActionForKey } from '../../../actions' -import { useMembership } from '../../../hooks' -import { subDaoInfosSelector } from '../../../recoil' +import { useCachedLoadingQuery, useMembership } from '../../../hooks' +import { daoQueries } from '../../../queries' import { ButtonLink } from '../../ButtonLink' import { DaoCard } from '../DaoCard' export const SubDaosTab = () => { - const { chain_id: chainId } = useChain() const daoInfo = useDaoInfoContext() const { getDaoPath, getDaoProposalPath } = useDaoNavHelpers() const { isMember = false } = useMembership(daoInfo) - const subDaos = useCachedLoading( - daoInfo.supportedFeatures[Feature.SubDaos] - ? subDaoInfosSelector({ chainId, coreAddress: daoInfo.coreAddress }) - : // Passing undefined here returns an infinite loading state, which is - // fine because it's never used. - undefined, + const queryClient = useQueryClient() + const subDaos = useCachedLoadingQuery( + { + ...daoQueries.subDaoInfos(queryClient, { + chainId: daoInfo.chainId, + coreAddress: daoInfo.coreAddress, + }), + enabled: !!daoInfo.supportedFeatures[Feature.SubDaos], + }, [] ) diff --git a/packages/stateful/hooks/useCachedLoadingQuery.ts b/packages/stateful/hooks/useCachedLoadingQuery.ts index 21e9446bf..5d85d3adf 100644 --- a/packages/stateful/hooks/useCachedLoadingQuery.ts +++ b/packages/stateful/hooks/useCachedLoadingQuery.ts @@ -1,4 +1,4 @@ -import { QueryKey, useQuery } from '@tanstack/react-query' +import { useQuery } from '@tanstack/react-query' import { useMemo } from 'react' import { useDeepCompareMemoize } from 'use-deep-compare-effect' @@ -9,25 +9,17 @@ import { LoadingData } from '@dao-dao/types' * Transform react-query query results into a cached loading object that * components expect. */ -export const useCachedLoadingQuery = < - TQueryFnData extends unknown, - TQueryKey extends QueryKey = QueryKey ->( - options: Omit< - Parameters< - typeof useQuery - >[0], - 'select' - >, +export const useCachedLoadingQuery = ( + options: Omit>[0], 'select'>, /** * Default value in case of an error. */ - defaultValue: TQueryFnData, + defaultValue: T, /** * Optionally call a function on error. */ onError?: (error: Error) => void -): LoadingData => { +): LoadingData => { const { isPending, isError, isRefetching, data, error } = useQuery(options) const onErrorRef = useUpdatingRef(onError) @@ -36,7 +28,7 @@ export const useCachedLoadingQuery = < // passed as the default value. const memoizedDefaultValue = useDeepCompareMemoize(defaultValue) - return useMemo((): LoadingData => { + return useMemo((): LoadingData => { if (isPending) { return { loading: true, diff --git a/packages/stateful/queries/dao.ts b/packages/stateful/queries/dao.ts index 7a028c3b7..f6f94e96b 100644 --- a/packages/stateful/queries/dao.ts +++ b/packages/stateful/queries/dao.ts @@ -34,13 +34,7 @@ import { fetchProposalModules } from '../utils' */ export const fetchDaoProposalModules = async ( queryClient: QueryClient, - { - chainId, - coreAddress, - }: { - chainId: string - coreAddress: string - } + { chainId, coreAddress }: DaoSource ): Promise => { const coreVersion = parseContractVersion( ( @@ -336,6 +330,44 @@ export const fetchDaoParentInfo = async ( throw new Error('Parent is not a DAO nor the chain governance module') } +/** + * Fetch DAO info for all of a DAO's SubDAOs. + */ +export const fetchSubDaoInfos = async ( + queryClient: QueryClient, + { chainId, coreAddress }: DaoSource +): Promise => { + const subDaos = await queryClient.fetchQuery( + daoDaoCoreQueries.listAllSubDaos(queryClient, { + chainId, + contractAddress: coreAddress, + }) + ) + + return await Promise.all( + subDaos.map(({ addr }) => + queryClient.fetchQuery( + daoQueries.info(queryClient, { chainId, coreAddress: addr }) + ) + ) + ) +} + +/** + * Fetch DAO info for all of a chain's SubDAOs. + */ +export const fetchChainSubDaoInfos = ( + queryClient: QueryClient, + { chainId }: { chainId: string } +): Promise => + Promise.all( + (getSupportedChainConfig(chainId)?.subDaos || []).map((coreAddress) => + queryClient.fetchQuery( + daoQueries.info(queryClient, { chainId, coreAddress }) + ) + ) + ) + export const daoQueries = { /** * Fetch DAO proposal modules. @@ -371,4 +403,26 @@ export const daoQueries = { queryKey: ['dao', 'parentInfo', options], queryFn: () => fetchDaoParentInfo(queryClient, options), }), + /** + * Fetch DAO info for all of a DAO's SubDAOs. + */ + subDaoInfos: ( + queryClient: QueryClient, + options: Parameters[1] + ) => + queryOptions({ + queryKey: ['dao', 'subDaoInfos', options], + queryFn: () => fetchSubDaoInfos(queryClient, options), + }), + /** + * Fetch DAO info for all of a chain's SubDAOs. + */ + chainSubDaoInfos: ( + queryClient: QueryClient, + options: Parameters[1] + ) => + queryOptions({ + queryKey: ['dao', 'chainSubDaoInfos', options], + queryFn: () => fetchChainSubDaoInfos(queryClient, options), + }), } diff --git a/packages/stateful/recoil/selectors/dao.ts b/packages/stateful/recoil/selectors/dao.ts index c1c56b9c5..2b3ce56ff 100644 --- a/packages/stateful/recoil/selectors/dao.ts +++ b/packages/stateful/recoil/selectors/dao.ts @@ -156,58 +156,6 @@ export const daoCardLazyDataSelector = selectorFamily< }, }) -export const subDaoInfosSelector = selectorFamily< - DaoInfo[], - WithChainId<{ coreAddress: string }> ->({ - key: 'subDaoInfos', - get: - ({ coreAddress: contractAddress, chainId }) => - ({ get }) => { - const subDaos = get( - DaoCoreV2Selectors.listAllSubDaosSelector({ - contractAddress, - chainId, - }) - ) - - return get( - waitForAll( - subDaos.map(({ chainId, addr }) => - daoInfoSelector({ - chainId, - coreAddress: addr, - }) - ) - ) - ) - }, -}) - -export const chainSubDaoInfosSelector = selectorFamily< - DaoInfo[], - { chainId: string } ->({ - key: 'chainSubDaoInfos', - get: - ({ chainId }) => - ({ get }) => { - const subDaos = getSupportedChainConfig(chainId)?.subDaos || [] - return subDaos.length - ? get( - waitForAll( - subDaos.map((coreAddress) => - daoInfoSelector({ - chainId, - coreAddress, - }) - ) - ) - ) - : [] - }, -}) - export const followingDaosWithProposalModulesSelector = selectorFamily< (DaoSource & { proposalModules: ProposalModule[]