TanStack Query for server data, XState for workflows. They will ask both.
1 · TanStack Query — what is it really?
🧠 in plain english
Imagine 10 friends all asking the same waiter the same question (“what's the soup of the day?”). Without TanStack Query, the waiter walks to the kitchen 10 times. With TanStack Query, he walks once, remembers the answer for 30 seconds, and gives the same answer to all 10 friends. Then the kitchen changes the soup. He says “my answer is stale,” quietly walks back to check, and updates everyone.
It's not a fetch wrapper. It's a cache with invalidation rules. You don't put server data in useState or Redux — you let TanStack manage it.
Step through it. Watch a second component subscribe to the same key — see the network call counter STAY AT 1 because TanStack dedupes. Then click invalidate, watch all subscribers refetch together:
TanStack Query — shared cache across components
ts
step 0 / 10
1// Component A mounts and useQuery(["wallet", 1])
2const { data } = useQuery({
3 queryKey: ["wallet", 1],
4 queryFn: () => api.getWallet(1),
5 staleTime: 30_000,
6});
7
8// Component B mounts and asks for the SAME key
9// → no new network call. subscribes to the same cache entry.
10
11// User clicks 'deposit $50'
12await api.deposit(50);
13queryClient.invalidateQueries(["wallet", 1]);
14// → all subscribers get the new data on refetch.
QueryClient cache
empty
Components subscribed
none
network calls
0
log
press ▶ play to step through this code, or use ← → to scrub.
The big idea: 10 components asking for the same key = ONE network request. They all subscribe to the same cache entry. That's why this beats useState + useEffect + fetch.
📣 say this in the interview
“Server data has cache rules — staleness, refetch, dedup, invalidation — Redux gives you none of that. TanStack Query is purpose-built. I'd only use Redux for cross-component client state, if at all.”
2 · Optimistic vs pessimistic — feel the UX
🧠 in plain english
Optimistic = clicking “like”. Heart fills instantly. If server fails, heart un-fills. No big deal. Pessimistic = clicking “send $50 to mom”. You wait for confirmation. If you optimistically showed “sent!”, then snapped back to “failed”, mom would call you angry.For PAM money operations: always pessimistic.
Same wallet, same fake server (~30% fail rate). Two strategies. Click both several times.
Optimistic (instant UI, can rollback)
$100
idle
Good for: likes, saves, UI prefs. BAD for money — user sees $150 then snap to $100.
Pessimistic (wait for server, never lies)
$100
idle
Slower, but trustworthy. Use for money operations always.
3 · XState — beat boolean soup
🧠 in plain english
Booleans are like switches. 5 switches = 32 possible combinations. Most are nonsense (“loading AND error?”). A state machine says “you can be in exactly ONE state at a time, and here are the legal transitions.” Like a board game — you can't be on Park Place AND in Jail at the same time.
Click events to transition. Invalid events are greyed out — that's the whole point. Watch the current state highlight, edges light up.
transition log
no transitions yet
📣 say this in the interview
“I'd model the KYC tier flow as an XState machine. It makes impossible states unreachable, the diagram itself is the spec compliance auditors can read, and tests become graph traversal.”
⚡ quick check
You're building a checkout flow with steps: cart → address → payment → review → confirmed. The user can go back from review to address, but not from confirmed back to anything. Which is the right tool?