-
-
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 | |
| } | |
| } |
I think it's basically the same as this? https://github.com/pingdotgg/zact
@dev-SR keep in mind that server actions can accept anything, not just FormData
@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.
formdata validation options, something like this???