-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
docs(solid-start): add Supabase example #5598
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| SUPABASE_URL=PleaseChangeMe | ||
| SUPABASE_ANON_KEY=PleaseChangeMe |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| node_modules | ||
| package-lock.json | ||
| yarn.lock | ||
|
|
||
| .DS_Store | ||
| .cache | ||
| .env | ||
| .vercel | ||
| .output | ||
| /build/ | ||
| /api/ | ||
| /server/build | ||
| /public/build# Sentry Config File | ||
| .env.sentry-build-plugin | ||
| /test-results/ | ||
| /playwright-report/ | ||
| /blob-report/ | ||
| /playwright/.cache/ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| **/build | ||
| **/public | ||
| pnpm-lock.yaml | ||
| routeTree.gen.ts |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| { | ||
| "files.watcherExclude": { | ||
| "**/routeTree.gen.ts": true | ||
| }, | ||
| "search.exclude": { | ||
| "**/routeTree.gen.ts": true | ||
| }, | ||
| "files.readonlyInclude": { | ||
| "**/routeTree.gen.ts": true | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,33 @@ | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| "name": "tanstack-solid-start-example-basic-supabase", | ||||||||||||||||||||||||||
| "version": "1.0.0", | ||||||||||||||||||||||||||
| "description": "", | ||||||||||||||||||||||||||
| "main": "index.js", | ||||||||||||||||||||||||||
| "type": "module", | ||||||||||||||||||||||||||
| "scripts": { | ||||||||||||||||||||||||||
| "dev": "vite dev", | ||||||||||||||||||||||||||
| "build": "vite build && tsc --noEmit", | ||||||||||||||||||||||||||
| "start": "vite start" | ||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix the production start script. Line 10 runs Apply this diff: "scripts": {
"dev": "vite dev",
"build": "vite build && tsc --noEmit",
- "start": "vite start"
+ "start": "vite preview"
},📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
|
Comment on lines
+8
to
+11
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix the production start script. Apply this diff: "scripts": {
"dev": "vite dev",
"build": "vite build && tsc --noEmit",
- "start": "vite start"
+ "start": "vite preview"
},📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
| "keywords": [], | ||||||||||||||||||||||||||
| "author": "", | ||||||||||||||||||||||||||
| "license": "ISC", | ||||||||||||||||||||||||||
| "dependencies": { | ||||||||||||||||||||||||||
| "@supabase/ssr": "^0.5.2", | ||||||||||||||||||||||||||
| "@supabase/supabase-js": "^2.48.1", | ||||||||||||||||||||||||||
| "@tanstack/solid-router": "^1.133.25", | ||||||||||||||||||||||||||
| "@tanstack/solid-router-devtools": "^1.133.25", | ||||||||||||||||||||||||||
| "@tanstack/solid-start": "^1.133.26", | ||||||||||||||||||||||||||
|
Comment on lines
+18
to
+20
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use workspace ranges for internal TanStack packages. Lines 18–20 pin internal packages to public versions, ignoring the repo guideline to consume them via the workspace linker. Update these entries to Apply this diff: "dependencies": {
"@supabase/ssr": "^0.5.2",
"@supabase/supabase-js": "^2.48.1",
- "@tanstack/solid-router": "^1.133.25",
- "@tanstack/solid-router-devtools": "^1.133.25",
- "@tanstack/solid-start": "^1.133.26",
+ "@tanstack/solid-router": "workspace:*",
+ "@tanstack/solid-router-devtools": "workspace:*",
+ "@tanstack/solid-start": "workspace:*",
"solid-js": "^1.9.9",
"redaxios": "^0.5.1"
},As per coding guidelines. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
| "solid-js": "^1.9.9", | ||||||||||||||||||||||||||
|
Comment on lines
+16
to
+21
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Use workspace ranges for internal TanStack packages. Apply this diff: "dependencies": {
"@supabase/ssr": "^0.5.2",
"@supabase/supabase-js": "^2.48.1",
- "@tanstack/solid-router": "^1.133.25",
- "@tanstack/solid-router-devtools": "^1.133.25",
- "@tanstack/solid-start": "^1.133.26",
+ "@tanstack/solid-router": "workspace:*",
+ "@tanstack/solid-router-devtools": "workspace:*",
+ "@tanstack/solid-start": "workspace:*",
"solid-js": "^1.9.9",
"redaxios": "^0.5.1"
},📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
| "redaxios": "^0.5.1" | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| "devDependencies": { | ||||||||||||||||||||||||||
| "@tailwindcss/postcss": "^4.1.15", | ||||||||||||||||||||||||||
| "@types/node": "^22.5.4", | ||||||||||||||||||||||||||
| "tailwindcss": "^4.1.6", | ||||||||||||||||||||||||||
| "typescript": "^5.7.2", | ||||||||||||||||||||||||||
| "vite": "^7.1.7", | ||||||||||||||||||||||||||
| "vite-plugin-solid": "^2.11.10", | ||||||||||||||||||||||||||
| "vite-tsconfig-paths": "^5.1.4" | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| { | ||
| "name": "", | ||
| "short_name": "", | ||
| "icons": [ | ||
| { | ||
| "src": "/android-chrome-192x192.png", | ||
| "sizes": "192x192", | ||
| "type": "image/png" | ||
| }, | ||
| { | ||
| "src": "/android-chrome-512x512.png", | ||
| "sizes": "512x512", | ||
| "type": "image/png" | ||
| } | ||
| ], | ||
| "theme_color": "#ffffff", | ||
| "background_color": "#ffffff", | ||
| "display": "standalone" | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,54 @@ | ||||||||||||||||||||||||||||||||||||||
| import type * as Solid from 'solid-js' | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| export function Auth(props: { | ||||||||||||||||||||||||||||||||||||||
| actionText: string | ||||||||||||||||||||||||||||||||||||||
| onSubmit: (e: Event) => void | ||||||||||||||||||||||||||||||||||||||
| status: 'pending' | 'idle' | 'success' | 'error' | ||||||||||||||||||||||||||||||||||||||
| afterSubmit?: Solid.JSX.Element | ||||||||||||||||||||||||||||||||||||||
| }) { | ||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||
| <div class="fixed inset-0 bg-white dark:bg-black flex items-start justify-center p-8"> | ||||||||||||||||||||||||||||||||||||||
| <div class="bg-white dark:bg-gray-900 p-8 rounded-lg shadow-lg"> | ||||||||||||||||||||||||||||||||||||||
| <h1 class="text-2xl font-bold mb-4">{props.actionText}</h1> | ||||||||||||||||||||||||||||||||||||||
| <form | ||||||||||||||||||||||||||||||||||||||
| onSubmit={(e) => { | ||||||||||||||||||||||||||||||||||||||
| e.preventDefault() | ||||||||||||||||||||||||||||||||||||||
| props.onSubmit(e) | ||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||
| class="space-y-4" | ||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||
| <div> | ||||||||||||||||||||||||||||||||||||||
| <label for="email" class="block text-xs"> | ||||||||||||||||||||||||||||||||||||||
| Username | ||||||||||||||||||||||||||||||||||||||
| </label> | ||||||||||||||||||||||||||||||||||||||
| <input | ||||||||||||||||||||||||||||||||||||||
| type="email" | ||||||||||||||||||||||||||||||||||||||
| name="email" | ||||||||||||||||||||||||||||||||||||||
| id="email" | ||||||||||||||||||||||||||||||||||||||
| class="px-2 py-1 w-full rounded border border-gray-500/20 bg-white dark:bg-gray-800" | ||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+21
to
+29
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix label text to match input type The label displays "Username" but the input has Apply this diff: <label for="email" class="block text-xs">
- Username
+ Email
</label>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||
| <div> | ||||||||||||||||||||||||||||||||||||||
| <label for="password" class="block text-xs"> | ||||||||||||||||||||||||||||||||||||||
| Password | ||||||||||||||||||||||||||||||||||||||
| </label> | ||||||||||||||||||||||||||||||||||||||
| <input | ||||||||||||||||||||||||||||||||||||||
| type="password" | ||||||||||||||||||||||||||||||||||||||
| name="password" | ||||||||||||||||||||||||||||||||||||||
| id="password" | ||||||||||||||||||||||||||||||||||||||
| class="px-2 py-1 w-full rounded border border-gray-500/20 bg-white dark:bg-gray-800" | ||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||
| <button | ||||||||||||||||||||||||||||||||||||||
| type="submit" | ||||||||||||||||||||||||||||||||||||||
| class="w-full bg-cyan-600 text-white rounded py-2 font-black uppercase" | ||||||||||||||||||||||||||||||||||||||
| disabled={props.status === 'pending'} | ||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||
| {props.status === 'pending' ? '...' : props.actionText} | ||||||||||||||||||||||||||||||||||||||
| </button> | ||||||||||||||||||||||||||||||||||||||
| {props.afterSubmit} | ||||||||||||||||||||||||||||||||||||||
| </form> | ||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| import { | ||
| ErrorComponent, | ||
| Link, | ||
| rootRouteId, | ||
| useMatch, | ||
| useRouter, | ||
| } from '@tanstack/solid-router' | ||
| import type { ErrorComponentProps } from '@tanstack/solid-router' | ||
|
|
||
| export function DefaultCatchBoundary({ error }: ErrorComponentProps) { | ||
| const router = useRouter() | ||
| const isRoot = useMatch({ | ||
| strict: false, | ||
| select: (state) => state.id === rootRouteId, | ||
| }) | ||
|
|
||
| console.error(error) | ||
|
|
||
| return ( | ||
| <div class="min-w-0 flex-1 p-4 flex flex-col items-center justify-center gap-6"> | ||
| <ErrorComponent error={error} /> | ||
| <div class="flex gap-2 items-center flex-wrap"> | ||
| <button | ||
| onClick={() => { | ||
| router.invalidate() | ||
| }} | ||
| class={`px-2 py-1 bg-gray-600 dark:bg-gray-700 rounded text-white uppercase font-extrabold`} | ||
| > | ||
| Try Again | ||
| </button> | ||
| {isRoot() ? ( | ||
| <Link | ||
| to="/" | ||
| class={`px-2 py-1 bg-gray-600 dark:bg-gray-700 rounded text-white uppercase font-extrabold`} | ||
| > | ||
| Home | ||
| </Link> | ||
| ) : ( | ||
| <Link | ||
| to="/" | ||
| class={`px-2 py-1 bg-gray-600 dark:bg-gray-700 rounded text-white uppercase font-extrabold`} | ||
| onClick={(e) => { | ||
| e.preventDefault() | ||
| window.history.back() | ||
| }} | ||
| > | ||
| Go Back | ||
| </Link> | ||
| )} | ||
| </div> | ||
| </div> | ||
| ) | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,72 @@ | ||||||||||||||||||||||||||||||||||||||||||
| import { useRouter } from '@tanstack/solid-router' | ||||||||||||||||||||||||||||||||||||||||||
| import { useServerFn } from '@tanstack/solid-start' | ||||||||||||||||||||||||||||||||||||||||||
| import { useMutation } from '../hooks/useMutation' | ||||||||||||||||||||||||||||||||||||||||||
| import { loginFn } from '../routes/_authed' | ||||||||||||||||||||||||||||||||||||||||||
| import { signupFn } from '../routes/signup' | ||||||||||||||||||||||||||||||||||||||||||
| import { Auth } from './Auth' | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| export function Login() { | ||||||||||||||||||||||||||||||||||||||||||
| const router = useRouter() | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| const loginMutation = useMutation({ | ||||||||||||||||||||||||||||||||||||||||||
| fn: loginFn, | ||||||||||||||||||||||||||||||||||||||||||
| onSuccess: async (ctx) => { | ||||||||||||||||||||||||||||||||||||||||||
| if (!ctx.data?.error) { | ||||||||||||||||||||||||||||||||||||||||||
| await router.invalidate() | ||||||||||||||||||||||||||||||||||||||||||
| router.navigate({ to: '/' }) | ||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+11
to
+20
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Use useServerFn(loginFn) on the client for consistency and SSR boundaries Calling a server function from a client component should go through useServerFn to avoid bundling/server boundary pitfalls. You already do this for signupFn. Align loginFn too. Apply this diff: - const loginMutation = useMutation({
- fn: loginFn,
+ const loginMutation = useMutation({
+ fn: useServerFn(loginFn),📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| const signupMutation = useMutation({ | ||||||||||||||||||||||||||||||||||||||||||
| fn: useServerFn(signupFn), | ||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||
| <Auth | ||||||||||||||||||||||||||||||||||||||||||
| actionText="Login" | ||||||||||||||||||||||||||||||||||||||||||
| status={loginMutation.status()} | ||||||||||||||||||||||||||||||||||||||||||
| onSubmit={(e) => { | ||||||||||||||||||||||||||||||||||||||||||
| const formData = new FormData(e.target as HTMLFormElement) | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| loginMutation.mutate({ | ||||||||||||||||||||||||||||||||||||||||||
| data: { | ||||||||||||||||||||||||||||||||||||||||||
| email: formData.get('email') as string, | ||||||||||||||||||||||||||||||||||||||||||
| password: formData.get('password') as string, | ||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||
| afterSubmit={ | ||||||||||||||||||||||||||||||||||||||||||
| loginMutation.data() ? ( | ||||||||||||||||||||||||||||||||||||||||||
| <> | ||||||||||||||||||||||||||||||||||||||||||
| <div class="text-red-400">{loginMutation.data()?.message}</div> | ||||||||||||||||||||||||||||||||||||||||||
| {loginMutation.data()?.error && | ||||||||||||||||||||||||||||||||||||||||||
| loginMutation.data()?.message === 'Invalid login credentials' ? ( | ||||||||||||||||||||||||||||||||||||||||||
| <div> | ||||||||||||||||||||||||||||||||||||||||||
| <button | ||||||||||||||||||||||||||||||||||||||||||
| class="text-blue-500" | ||||||||||||||||||||||||||||||||||||||||||
| onClick={(e) => { | ||||||||||||||||||||||||||||||||||||||||||
| const formData = new FormData( | ||||||||||||||||||||||||||||||||||||||||||
| (e.target as HTMLButtonElement).form!, | ||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| signupMutation.mutate({ | ||||||||||||||||||||||||||||||||||||||||||
| data: { | ||||||||||||||||||||||||||||||||||||||||||
| email: formData.get('email') as string, | ||||||||||||||||||||||||||||||||||||||||||
| password: formData.get('password') as string, | ||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||
| type="button" | ||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||
| Sign up instead? | ||||||||||||||||||||||||||||||||||||||||||
| </button> | ||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||
| ) : null} | ||||||||||||||||||||||||||||||||||||||||||
| </> | ||||||||||||||||||||||||||||||||||||||||||
| ) : null | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| import { Link } from '@tanstack/solid-router' | ||
| import type * as Solid from 'solid-js' | ||
|
|
||
| export function NotFound({ children }: { children?: Solid.JSX.Element }) { | ||
| return ( | ||
| <div class="space-y-2 p-2"> | ||
| <div class="text-gray-600 dark:text-gray-400"> | ||
| {children || <p>The page you are looking for does not exist.</p>} | ||
| </div> | ||
| <p class="flex items-center gap-2 flex-wrap"> | ||
| <button | ||
| onClick={() => window.history.back()} | ||
| class="bg-emerald-500 text-white px-2 py-1 rounded uppercase font-black text-sm" | ||
| > | ||
| Go back | ||
| </button> | ||
| <Link | ||
| to="/" | ||
| class="bg-cyan-600 text-white px-2 py-1 rounded uppercase font-black text-sm" | ||
| > | ||
| Start Over | ||
| </Link> | ||
| </p> | ||
| </div> | ||
| ) | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,42 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { createSignal } from 'solid-js' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export function useMutation<TVariables, TData, TError = Error>(opts: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn: (variables: TVariables) => Promise<TData> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onSuccess?: (ctx: { data: TData }) => void | Promise<void> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [submittedAt, setSubmittedAt] = createSignal<number | undefined>() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [variables, setVariables] = createSignal<TVariables | undefined>() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [error, setError] = createSignal<TError | undefined>() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [data, setData] = createSignal<TData | undefined>() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [status, setStatus] = createSignal< | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'idle' | 'pending' | 'success' | 'error' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| >('idle') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const mutate = async (variables: TVariables): Promise<TData | undefined> => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setStatus('pending') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setSubmittedAt(Date.now()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setVariables(() => variables) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const result = await opts.fn(variables) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await opts.onSuccess?.({ data: result }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setStatus('success') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setError(undefined) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setData(() => result) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return result | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (err) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setStatus('error') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setError(() => err as TError) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return undefined | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+15
to
+31
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Prevent stale data and clear state when re-submitting On error, data remains from a previous success. Also clear last error/data when entering pending. Apply this diff: const mutate = async (variables: TVariables): Promise<TData | undefined> => {
- setStatus('pending')
+ setStatus('pending')
setSubmittedAt(Date.now())
setVariables(() => variables)
+ setError(undefined)
+ setData(undefined)
try {
- const result = await opts.fn(variables)
- await opts.onSuccess?.({ data: result })
+ const result = await opts.fn(variables)
+ // Set data before onSuccess so UI can react even if onSuccess throws
+ setData(() => result)
+ await opts.onSuccess?.({ data: result })
setStatus('success')
- setError(undefined)
- setData(() => result)
return result
} catch (err) {
setStatus('error')
setError(() => err as TError)
+ setData(undefined)
return undefined
}
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| variables, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| submittedAt, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mutate, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix the comment placement on line 15.
The comment
# Sentry Config Fileis appended to the end of/public/buildon line 15, which breaks readability and may cause parsing issues. Move the comment to its own line above.env.sentry-build-plugin.📝 Committable suggestion
🤖 Prompt for AI Agents