Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for specifying the fragment name in useFragmentWhere #79

Merged
merged 1 commit into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
8 changes: 6 additions & 2 deletions src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()}`;
}
22 changes: 15 additions & 7 deletions src/hooks/useFragmentTypePolicyFieldName.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
12 changes: 7 additions & 5 deletions src/hooks/useFragmentWhere.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -16,6 +15,7 @@ export default function useFragmentWhere<FragmentType>(fragment: DocumentNode, o
returnPartialData?: boolean;
limit?: number;
orderBy?: FragmentWhereOrderBy;
fragmentName?: string;
}) {
const filter = options?.filter;
const filterVarRef = useRef(makeVar<FragmentWhereFilter<FragmentType> | undefined>(filter));
Expand All @@ -32,7 +32,9 @@ export default function useFragmentWhere<FragmentType>(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<FragmentType[]>([]);

useEffect(() => {
Expand All @@ -53,7 +55,7 @@ export default function useFragmentWhere<FragmentType>(fragment: DocumentNode, o
}
}, [orderBy]);

const query = useOnce(() => buildWatchFragmentWhereQuery({
const query = useMemo(() => buildWatchFragmentWhereQuery({
filter,
filterVar,
limitVar,
Expand All @@ -62,7 +64,7 @@ export default function useFragmentWhere<FragmentType>(fragment: DocumentNode, o
fieldName,
cache,
policies: cache.policies,
}));
}), [fieldName]);

const result = useGetQueryDataByFieldName<FragmentType[]>(query, fieldName, options);

Expand Down
12 changes: 10 additions & 2 deletions src/hooks/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useRef } from 'react';
import { useEffect, useRef } from 'react';
import isFunction from "lodash/isFunction";

export function useOnce<T>(value: T | (() => T)): T {
Expand All @@ -13,4 +13,12 @@ export function useOnce<T>(value: T | (() => T)): T {
hasCachedValueRef.current = true;
}
return valueRef.current as T;
}
}

export const usePrevious = <T>(value: T): T | null => {
const ref = useRef<T | null>(null);
useEffect(() => {
ref.current = value;
});
return ref.current;
};
Loading