Skip to content

Commit

Permalink
useFragment: keep current result on a memoized mutable object
Browse files Browse the repository at this point in the history
  • Loading branch information
phryneas committed Sep 3, 2024
1 parent 3fdc381 commit 9c26892
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 22 deletions.
5 changes: 5 additions & 0 deletions .changeset/fuzzy-shrimps-call.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@apollo/client": patch
---

Fix a bug where `useFragment` did not re-render as expected
4 changes: 2 additions & 2 deletions .size-limits.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"dist/apollo-client.min.cjs": 40252,
"import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 33059
"dist/apollo-client.min.cjs": 40242,
"import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 33058
}
37 changes: 17 additions & 20 deletions src/react/hooks/useFragment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,30 +75,28 @@ function _useFragment<TData = any, TVars = OperationVariables>(
[cache, from]
);

const resultRef = React.useRef<UseFragmentResult<TData>>();
const stableOptions = useDeepMemo(() => ({ ...rest, from: id! }), [rest, id]);

// Since .next is async, we need to make sure that we
// get the correct diff on the next render given new diffOptions
const currentDiff = React.useMemo(() => {
const diff = React.useMemo(() => {
const { fragment, fragmentName, from, optimistic = true } = stableOptions;

return diffToResult(
cache.diff<TData>({
...stableOptions,
returnPartialData: true,
id: from,
query: cache["getFragmentDoc"](fragment, fragmentName),
optimistic,
})
);
return {
result: diffToResult(
cache.diff<TData>({
...stableOptions,
returnPartialData: true,
id: from,
query: cache["getFragmentDoc"](fragment, fragmentName),
optimistic,
})
),
};
}, [stableOptions, cache]);

// Used for both getSnapshot and getServerSnapshot
const getSnapshot = React.useCallback(
() => resultRef.current || currentDiff,
[currentDiff]
);
const getSnapshot = React.useCallback(() => diff.result, [diff]);

return useSyncExternalStore(
React.useCallback(
Expand All @@ -109,9 +107,9 @@ function _useFragment<TData = any, TVars = OperationVariables>(
// Since `next` is called async by zen-observable, we want to avoid
// unnecessarily rerendering this hook for the initial result
// emitted from watchFragment which should be equal to
// `currentDiff`.
if (equal(result, currentDiff)) return;
resultRef.current = result;
// `diff.result`.
if (equal(result, diff.result)) return;
diff.result = result;
// If we get another update before we've re-rendered, bail out of
// the update and try again. This ensures that the relative timing
// between useQuery and useFragment stays roughly the same as
Expand All @@ -121,12 +119,11 @@ function _useFragment<TData = any, TVars = OperationVariables>(
},
});
return () => {
resultRef.current = void 0;
subscription.unsubscribe();
clearTimeout(lastTimeout);
};
},
[cache, stableOptions, currentDiff]
[cache, stableOptions, diff]
),
getSnapshot,
getSnapshot
Expand Down

0 comments on commit 9c26892

Please sign in to comment.