-
-
Save mishushakov/c03f16942ba4af4c304996de22d72730 to your computer and use it in GitHub Desktop.
'use client' | |
import { test } from './server' | |
import { useServerAction } from './hook' | |
export default function Home() { | |
const { data, loading, error, execute: testAction } = useServerAction(test) | |
if (loading) return <div>Loading...</div> | |
if (error) return <div>Error: {error.message}</div> | |
return ( | |
<> | |
<form action={testAction}> | |
<input type="text" name='name' /> | |
<button type="submit">Submit</button> | |
</form> | |
<div>{data}</div> | |
</> | |
) | |
} |
'use server' | |
export async function test(form: FormData) { | |
const name = form.get('name') | |
return name?.toString() | |
} |
import { useState } from 'react' | |
type AsyncFunction = (...args: any) => Promise<any> | |
export function useServerAction<T extends AsyncFunction>(action: T) { | |
const [loading, setLoading] = useState(false) | |
const [error, setError] = useState<Error | null | undefined>(null) | |
const [data, setData] = useState<Awaited<ReturnType<T>> | null | undefined>(null) | |
async function execute (...payload: Parameters<T>) { | |
setLoading(true) | |
setError(null) | |
setData(null) | |
try { | |
const res = await action(payload) | |
setData(res) | |
} catch (e: any) { | |
setError(e) | |
} | |
setLoading(false) | |
} | |
return { | |
data, | |
loading, | |
error, | |
execute | |
} | |
} |
@amosbastian similar concept, yes
I have a question. If you execute revalidatePath
in the action, does await action(payload)
wait for the new data (e.g. if you reload a list)?
This isn't explained in the docs, but I think it doesn't. I think that's why it recommends wrapping it in startTransition
, which presumably somehow waits for it.
I have had some cases where there's a mismatch between my loading
state and the timing where the data is updated (which is clear after using a sleep in the page that renders the new data).
@ChrisVilches good question. I'm not an expert, so maybe ask @dan_abramov on twitter
I think it's basically the same as this? https://github.com/pingdotgg/zact
This package is good. Great work by @t3dotgg
formdata validation options, something like this???
import { useState } from 'react'; import { z } from 'zod'; function formDataToObject(formData: FormData): { [key: string]: any } { const object: { [key: string]: any } = {}; formData.forEach((value, key) => { if (!object.hasOwnProperty(key)) { object[key] = value; } else if (Array.isArray(object[key])) { object[key].push(value); } else { object[key] = [object[key], value]; } }); return object; } type AsyncFunction = (...args: any) => Promise<any>; export function getPayloadSchema() { return z.object({ // Define your payload schema using Zod's validation functions // For example: name: z.string().min(3).max(20) }); } export function useServerAction<T extends AsyncFunction>(action: T, validator: z.Schema) { const [loading, setLoading] = useState(false); const [error, setError] = useState<Error | null | undefined>(null); const [data, setData] = useState<Awaited<ReturnType<T>> | null | undefined>(null); async function execute(payload: FormData) { setLoading(true); setError(null); setData(null); try { // Validate the payload using the provided Zod schema const objectData = formDataToObject(payload); const validatedPayload = validator.parse(objectData); const res = await action(validatedPayload); setData(res); } catch (e: any) { setError(e); } setLoading(false); } return { data, loading, error, execute }; }'use client'; import { useServerAction } from '@/lib/useServerAction'; import { test } from './action'; import { z } from 'zod'; export default function Form() { const { data, loading, error, execute: testAction } = useServerAction( test, z.object({ name: z.string().min(3).max(20) }) ); if (loading) return <div>Loading...</div>; if (error) return <div>Error: {error.message}</div>; return ( <form action={testAction} className='flex flex-col justify-center items-center h-full'> <input placeholder='Enter a value' name='name' /> <button type='submit'>Add</button> <div>{JSON.stringify(data, null, 2)}</div> </form> ); }'use server'; export async function test({ name }: { name: string }) { return name ; }
the payload formData is most likely empyt.
@dev-SR keep in mind that server actions can accept anything, not just FormData