From 2e1af24bf35a72de18dfd4b622eaf0a1b9538d18 Mon Sep 17 00:00:00 2001 From: Dan Reynolds Date: Tue, 26 Mar 2024 10:40:21 -0400 Subject: [PATCH] add support for specifying the fragment name to use --- CHANGELOG.md | 4 ++++ package.json | 2 +- src/helpers.ts | 8 ++++++-- src/hooks/useFragmentTypePolicyFieldName.ts | 22 ++++++++++++++------- src/hooks/useFragmentWhere.ts | 12 ++++++----- src/hooks/utils.ts | 12 +++++++++-- 6 files changed, 43 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8109cd..4e0e8be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +3.3.0 (Dan Reynolds) + +Add support for specifying the fragment name to use in the `useFragmentWhere` API. + 3.2.0 (Dan Reynolds) Add support for an `orderBy` field to `fragmentWhere` API. diff --git a/package.json b/package.json index 33daa84..662d00a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@nerdwallet/apollo-cache-policies", - "version": "3.2.0", + "version": "3.3.0", "description": "An extension to the InMemoryCache from Apollo that adds additional cache policies.", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/src/helpers.ts b/src/helpers.ts index feecd7e..2d6bc30 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -40,6 +40,10 @@ export function fieldNameFromStoreName(storeFieldName: string) { // a leading prefix in order to distinguish it as a fragment policy and // prevent the UUID from being trimmed by the `fieldNameFromStoreFieldName` regex. // https://github.com/apollographql/apollo-client/blob/d9a1039d36801d450a79cb56870f0a351044254b/src/cache/inmemory/helpers.ts#L80 -export function generateFragmentFieldName() { - return `-fragment-${v4()}`; +export function generateFragmentFieldName({ + fragmentName, +}: { + fragmentName?: string; +} = {}) { + return `-fragment-${fragmentName ?? v4()}`; } \ No newline at end of file diff --git a/src/hooks/useFragmentTypePolicyFieldName.ts b/src/hooks/useFragmentTypePolicyFieldName.ts index 42910ba..8ff061c 100644 --- a/src/hooks/useFragmentTypePolicyFieldName.ts +++ b/src/hooks/useFragmentTypePolicyFieldName.ts @@ -1,18 +1,26 @@ import { useApolloClient } from "@apollo/client"; -import { useEffect, useRef } from "react" +import { useEffect, useMemo } from "react" import { InvalidationPolicyCache } from "../cache"; import { generateFragmentFieldName } from "../helpers"; +import { usePrevious } from "./utils"; // Creates a field name to be used for a dynamically added field policy. -export const useFragmentTypePolicyFieldName = (): string => { - const { current: fieldName } = useRef(generateFragmentFieldName()); +export const useFragmentTypePolicyFieldName = ({ + fragmentName, +}: { + fragmentName?: string; +} = {}): string => { + const fieldName = useMemo(() => generateFragmentFieldName({ fragmentName }), [fragmentName]); + const prevFieldName = usePrevious(fieldName); const client = useApolloClient(); useEffect(() => - // @ts-ignore After the component using the hook is torn down, remove the dynamically added type policy - // for this hook from the type policies list. - () => delete (client.cache as InvalidationPolicyCache).policies.typePolicies.Query.fields[fieldName], - [], + () => { + // @ts-ignore After the component using the hook is torn down, remove the dynamically added type policy + // for this hook from the type policies list. + delete (client.cache as InvalidationPolicyCache).policies.typePolicies.Query.fields[prevFieldName]; + }, + [fieldName, prevFieldName], ); return fieldName; diff --git a/src/hooks/useFragmentWhere.ts b/src/hooks/useFragmentWhere.ts index 789ad5e..bfc7c00 100644 --- a/src/hooks/useFragmentWhere.ts +++ b/src/hooks/useFragmentWhere.ts @@ -1,10 +1,9 @@ import { getApolloContext } from '@apollo/client'; -import { useContext, useEffect, useRef } from 'react'; +import { useContext, useEffect, useMemo, useRef } from 'react'; import { DocumentNode } from 'graphql'; import InvalidationPolicyCache from '../cache/InvalidationPolicyCache'; import { buildWatchFragmentWhereQuery } from '../client/utils'; import { FragmentWhereFilter, FragmentWhereOrderBy } from '../cache/types'; -import { useOnce } from './utils'; import { useFragmentTypePolicyFieldName } from './useFragmentTypePolicyFieldName'; import { useGetQueryDataByFieldName } from './useGetQueryDataByFieldName'; import { makeVar } from '@apollo/client'; @@ -16,6 +15,7 @@ export default function useFragmentWhere(fragment: DocumentNode, o returnPartialData?: boolean; limit?: number; orderBy?: FragmentWhereOrderBy; + fragmentName?: string; }) { const filter = options?.filter; const filterVarRef = useRef(makeVar | undefined>(filter)); @@ -32,7 +32,9 @@ export default function useFragmentWhere(fragment: DocumentNode, o const context = useContext(getApolloContext()); const client = context.client; const cache = client?.cache as unknown as InvalidationPolicyCache; - const fieldName = useFragmentTypePolicyFieldName(); + const fieldName = useFragmentTypePolicyFieldName({ + fragmentName: options?.fragmentName, + }); const emptyValue = useRef([]); useEffect(() => { @@ -53,7 +55,7 @@ export default function useFragmentWhere(fragment: DocumentNode, o } }, [orderBy]); - const query = useOnce(() => buildWatchFragmentWhereQuery({ + const query = useMemo(() => buildWatchFragmentWhereQuery({ filter, filterVar, limitVar, @@ -62,7 +64,7 @@ export default function useFragmentWhere(fragment: DocumentNode, o fieldName, cache, policies: cache.policies, - })); + }), [fieldName]); const result = useGetQueryDataByFieldName(query, fieldName, options); diff --git a/src/hooks/utils.ts b/src/hooks/utils.ts index a996efd..5c9702f 100644 --- a/src/hooks/utils.ts +++ b/src/hooks/utils.ts @@ -1,4 +1,4 @@ -import { useRef } from 'react'; +import { useEffect, useRef } from 'react'; import isFunction from "lodash/isFunction"; export function useOnce(value: T | (() => T)): T { @@ -13,4 +13,12 @@ export function useOnce(value: T | (() => T)): T { hasCachedValueRef.current = true; } return valueRef.current as T; -} \ No newline at end of file +} + +export const usePrevious = (value: T): T | null => { + const ref = useRef(null); + useEffect(() => { + ref.current = value; + }); + return ref.current; +};