Prompt
Write a React Native component that uses Apollo Client's useQuery hook to fetch a list of books (id, title, author) and display them — handling the loading and error states before touching data.
Solution
useQuery is declarative data fetching: the component states what it needs and Apollo owns the lifecycle — request, cache, dedupe, re-render. The gql tag is a tagged template — a function receiving the string parts and interpolated values separately — which parses the query into an AST once at module load, not per render. That's the same mechanism behind styled-components, and naming it earns points.
The three-state ladder — loading, then error, then data — isn't boilerplate, it's the contract: touch data.books before checking the first two and the first slow network turns into a crash on undefined.
The senior detail is why the query selects id. Apollo's cache is normalized: objects are stored flat, keyed by __typename plus id. Query the id and a mutation returning the same book updates every screen showing it automatically; omit it and the cache can't normalize, so updates silently stop propagating. keyExtractor reusing that id is the free by-product, not the reason.
Also worth saying: loading is only true on the first fetch by default — refetches need notifyOnNetworkStatusChange if you want a spinner — and error can coexist with partial data.
Red flag: mirroring data into local state with useEffect + useState. The normalized cache is the state; the copy goes stale the moment a mutation updates the original.
Say it: "useQuery gives me the three render states declaratively, and because Apollo normalizes by __typename plus id, selecting the id is what makes cache updates propagate — not just a keyExtractor convenience."