Description
Issue Description
I am querying a GraphQL endpoint with dynamic variables. I expect to transform the data inside the onCompleted
function, and set a variable based on the dynamic variable. However, the Apollo client is caching the old onCompleted
callback, so that it's one out of date, which gives me incorrect data.
To show this, I've written an MRE which console logs inside the onCompleted
function. From the parent, I pass a prop down which is printed in the log statement. The prop is a number and each re-render increases it by one. Instead of seeing n
in my logs, though, I see n - 1
for everything after the first one.
Render Count | Prop Value | Expected Log Value | Actual Log Value |
---|---|---|---|
Render #1 | 0 | Completed callback: 0 | Completed callback: 0 |
Render #2 | 1 | Completed callback: 1 | Completed callback: 0 |
Render #3 | 2 | Completed callback: 2 | Completed callback: 1 |
Render #4 | 3 | Completed callback: 3 | Completed callback: 2 |
Render #5 | 4 | Completed callback: 4 | Completed callback: 3 |
MRE code
index.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client';
const client = new ApolloClient({
uri: 'https://flyby-router-demo.herokuapp.com/',
cache: new InMemoryCache(),
});
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<ApolloProvider client={client}>
<App />
</ApolloProvider>
</React.StrictMode>
)
App.tsx
import "./App.css";
import { useQuery, gql } from "@apollo/client";
import { useState } from "react";
const GET_LOCATION = gql`
query GetLocationDetails($locationId: ID!) {
location(id: $locationId) {
id
name
description
photo
overallRating
reviewsForLocation {
id
comment
rating
}
}
}
`;
function DisplayLocations({ lastStateCount }) {
const { loading, error, data } = useQuery(GET_LOCATION, {
variables: {
locationId: "loc-1",
ignore: lastStateCount,
},
onCompleted: (data) => {
console.log("Completed callback:", lastStateCount);
},
});
if (loading) return <p>Loading...</p>;
if (error) return <p>Error : {error.message}</p>;
return [data.location].map(({ id, name, description, photo }) => (
<div key={id}>
<h3>
{name} ({id})
</h3>
<img width="400" height="250" alt="location-reference" src={`${photo}`} />
<br />
<b>About this location:</b>
<p>{description}</p>
<br />
</div>
));
}
export default function App() {
const [state, setState] = useState(0);
return (
<main>
<button onClick={() => setState(state + 1)}>
Click me to increase the counters.
</button>
<p>
<b>Note:</b> The below text is wrong for the first render. Click the
button for the text to match the console logs. The first one works, but
it fails from there on out.
</p>
Expected console log: "Complete callback: <b>{state}</b>"
<br />
What you see: "Complete callback: <b>{state - 1}</b>"
<br />
<br />
<DisplayLocations lastStateCount={state} />
</main>
);
}
package.json
"dependencies": {
"@apollo/client": "^3.12.8"
},
If it'll load, I've also made a Repl here: https://replit.com/@DavidAntonucci/ApolloBugMRE#src/App.tsx
Link to Reproduction
https://replit.com/@DavidAntonucci/ApolloBugMRE#src/App.tsx
Reproduction Steps
No response
@apollo/client
version
3.12.8