demo(recipe): swap the hand-rolled hooks for ReactFire - #798
Open
tyler-reitz wants to merge 3 commits into
Open
Conversation
Replaces the app's own Firestore and auth plumbing with ReactFire hooks, so the diff against recipe-demo is the comparison itself. - src/lib/use-recipes.ts (42 lines) becomes useFirestoreCollectionData with initialData, keeping the server-fetched list as the seed - src/lib/session-context.tsx (17 lines) is deleted; useUser and useSigninCheck are called from the components that need them - src/lib/providers.tsx wires FirebaseAppProvider, FirestoreProvider and AuthProvider to the existing module-level instances The dependency is a packed build of main at ac3ccf9, committed under recipe-demo/ with a gitignore exception. Published 4.2.6 predates #779, and without it this app does not build: SessionNav calls useUser from the root layout, so prerendering /_not-found and /signin fails with "Missing getServerSnapshot". Measured both ways, with the patched build restored afterwards. RecipeBrowser loses its error branch because useObservable on main re-throws rather than returning status 'error', and the recipe list needs a cast because useFirestoreCollectionData's generic requires a typed Query. Both are findings for the PR description rather than problems to work around here.
# Conflicts: # recipe-demo/src/lib/use-recipes.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Swaps the vanilla app's hand-rolled Firestore and auth plumbing for ReactFire. The diff against
recipe-demois the deliverable: same app, same behavior, nothing added.Not a merge candidate. This branch and #797 exist to be compared and read, not landed.
What ReactFire replaces
src/lib/use-recipes.ts, 44 lines of hydrate-then-subscribeuseFirestoreCollectionData(query, { idField, initialData })src/lib/session-context.tsx, 18 lines of context overonAuthStateChangeduseUser()anduseSigninCheck(), called directlyNine files under
src/pluspackage.json, +42 / -94. The base branch's commit message predicts five files; that assumed keeping both modules as thin wrappers over ReactFire, which was rejected because it preserves the abstraction ReactFire is meant to obviate and hidesuseSigninCheckentirely.src/lib/providers.tsx(16 lines) is new and wires the three providers to the app's existing module-level instances.recipes.ts,types.ts,ai.ts,firebase.ts,RecipeList.tsxand all three pages are untouched.The homepage still fetches on the server with the Firebase JS SDK, not the Admin SDK, and hands the result to a client component as
initialData. The recipe list is public, so no identity is needed and the security rules stay in play.Findings
This app does not build against published reactfire.
SessionNavcallsuseUserfrom the root layout, so every prerendered route hits a reactfire hook:Measured both ways: patched build → 8
<article>in the server HTML; published 4.2.6 → build fails; patched build restored → 8 again. #779 is therefore a hard prerequisite for this demo, not an improvement to it.Error handling is where reactfire is worse.
RecipeBrowserhad an error branch rendering the failure inline.useObservableonmainre-throws unconditionally,suspense: falseor not, so that UI cannot work and the branch is deleted rather than left dead. The vanilla hook kept the page up and showed the error; this version unmounts to the nearest boundary. #735 fixes exactly this and isv5-only, so it is the obvious candidate for a round two.A cast is unavoidable.
useFirestoreCollectionData<Recipe>requires aQuery<Recipe>, butrecipeQueryreturnsQuery<DocumentData>, so the generic moves the error rather than solving it. Typing it properly means awithConverterinrecipes.ts, which would put Firestore types back into the file the swap is meant to leave alone.data as Recipe[]at the call site is the smaller cost.Hydrated versus live is off the status enum. The distinction the Firebase SSR blog post's hand-rolled
useSerializedQuerytracks is not something reactfire'sstatuscan carry. Measured against the emulator withinitialDatapassed: while the server data is still on screen,statusis alreadysuccessandhasEmittedis alreadytrue, and onlyfirstValuePromiseresolving marks the first live snapshot. So the capability exists, but only by awaiting a promise and mirroring it into local state, which is the bookkeeping the swap is meant to remove. The vanilla branch previously declared four feed states while rendering two; both sides now carry the same three, and this is the reason why.Two pages are byte-identical to vanilla, which is also a result:
signin/page.tsx, since there is no sign-in wrapper, andcreate-recipe/page.tsx, since there is no AI Logic binding and the page calls the SDK directly.RecipeCardchanges only its user lookup; the like itself is plainupdateDoceither way.RequireAuthbarely shrinks, becauseuseSigninCheckstill returns a status and the loading-then-redirect dance survives.The dependency
reactfireis a packed build ofmainatac3ccf9, committed underrecipe-demo/with a.gitignoreexception. Regenerate withnpx tsc && npx vite build && npm packat that commit. Afile:..link was rejected because npm symlinks the repo root in, bringing a second React with it.Verification
Against the emulator suite, controls run in both directions:
<article>with<script>blocks stripped, negative control on an absent titleLike (0)→Liked (1), page sentinel survived so no reloadLike (1)with no interaction, and rendered as "Like" rather than "Liked" because a different uid made it/create-recipesigned out redirects to/signin?next=%2Fcreate-recipetscNot verified:
/create-recipegeneration, which needs a real project with AI Logic enabled. Same gap as the vanilla half.AGENTS.mdandCLAUDE.mdin this folder are written bynext devwhen it detects an AI coding agent, never bynext build, andagentRules: falseturns the behaviour off. They are Next's own instructions to coding agents, not ours, and are committed so the tree stays clean.