Coder Social home page Coder Social logo

zsa's People

Contributors

andresgutgon avatar dependabot[bot] avatar github-actions[bot] avatar idopesok avatar ingadi avatar mahmudulmr19 avatar mjbalcueva avatar soulchild avatar turbobot-temp avatar virus231 avatar webdevcody avatar westernconcrete avatar yantakus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

zsa's Issues

Send zod validation errors

Hello!

I've been working on my own trpc like server action library then came across this, it's basically the exact api I had, so thinking I might as well use this.

The only thing im missing from this library is the ability to return formatted validation errors that comes from zod i.e. result.error.flatten().fieldErrors when safeParse fails.

Looking at your code I see that you just throw a custom Error when this fails but the response to the client is stringified json, I guess I could parse it on the frontend but my ideal api would be something like this:

export function LoginForm() {
  const { execute, data, error } = useServerAction(login)
  return (
    <form action={execute} className="space-y-2">
      <FormField
        required
        minLength={3}
        label="Email address"
        name="email"
        errors={error?.fieldErrors?.email}
      />
      <FormField
        required
        minLength={8}
        label="Password"
        name="password"
        type="password"
        errors={error?.fieldErrors?.password}
        placeholder="********"
      />
      <FormError error={error.formError} />
      <FormButton className="w-full">Login</FormButton>
    </form>
  )

Wondering if you've run into similar needs or if theres another way of achieving something similar this? trpc has the formatError function which allows you to customise the error response so I could add fieldErrors as a custom key, and formError for general errors. What do you think?

Thanks for the good work!

Allow action to throw error

It would be convenient to add a helper chaining method (e.g. .throw()) so that the server action would throw the error instead of returning it.

Use cases: the error.tsx and <ErrorBoundary /> components would automatically work if the server action throws the error, now I have to manually re-throw it

// page.tsx
const [data, err] = await action()
if (err) {
 throw new Error(err)
}

if with the chaining method I could just do

const action2 = action.throw()
const data = await action2()

revalidatePath in server actions causes nothing to happen on success

This worked before I upgraded today. Might be related to #91 ?

"zsa": "^0.3.2",
"zsa-react": "^0.1.4"

    // Server action
    revalidatePath(`/admin/shop/categories/${categoryId}`)

    return {
      message: 'Item added',
    }
  const onClick = async () => {
    console.debug('Fired')
    const [, err] = await execute({
      catalogItemId: Number(item.objectID),
      categoryId,
    })

    console.debug('This works if err')
    console.debug('err', err)

    if (err) {
      toast({
        title: 'Error',
        description: 'Error adding item',
      })
      return
    }

    console.log('Never called if revalidatePath is hit')

    toast({
      title: 'Item added โœ…',
      description: 'Item added!',
    })
  }
Screen Shot 2024-06-06 at 10 51 06 AM

[feature] openapi router

fuzzy vision:

const router = createRouter()

router.get("/posts", getPostAction)

router.post("/posts/{postId}/replies", createReplyAction)

export { GET, POST } = createRouteHandlers(router)

It should also generate the OpenAPI document

prefetchQuery, useSuspenseQuery with zsa

Is there any way to use zsa-react-query with prefetchQuery and useSuspenseQuery?
It would be nice to be able to prefetch data with validation, type safety and also to stay with react-query ecosystem

Customise error format

Hi @IdoPesok,

I'm throwing a ZSAError within a serverAction.

    if (!organization) {
      throw new ZSAError('NOT_FOUND', 'Organization not found');
    }

Can we customise the error payload please? I would like to exclude the stack trace and only return message and code. Seems a bit redundant to have both data and message with the same value.

{
    "data": "Organization not found",
    "name": "Error",
    "stack": "\"Error: Organization not found\\n    at $$ACTION_0 (webpack-internal:///(rsc)/./src/lib/actions/organizations/actions.ts:35:15)\\n    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\\n    at async wrapper (webpack-internal:///(rsc)/../../node_modules/.pnpm/[email protected]/node_modules/zsa/dist/index.mjs:393:22)\\n    at async getResponseFromAction (webpack-internal:///(rsc)/../../node_modules/.pnpm/[email protected]/node_modules/zsa-openapi/dist/index.mjs:2043:25)\\n    at async handler (webpack-internal:///(rsc)/../../node_modules/.pnpm/[email protected]/node_modules/zsa-openapi/dist/index.mjs:2149:12)\\n",
    "message": "Organization not found",
    "code": "NOT_FOUND"
}

Ideally I would like to have:

{
  "code": "NOT_FOUND",
  "message": "Organization not found"
}

or for a validation error:

{
  "code": "INPUT_PARSE_ERROR",
  "message": "Invalid uuid",
  "formattedErrors": { ... }
}

What do you think?

Error messages swallowed up

I'm not sure if this was intended, but if the handler throws a normal Error, it seems like the error message is replaced with {}. It would be nice if normal exceptions would be wrapped and sent back to the UI as expected.

export const createGroupAction = authenticatedAction
  .createServerAction()
  .input(schema)
  .handler(async ({ input: { name, description }, ctx: { user } }) => {
    throw new Error("GG");
  });
Screenshot 2024-05-30 at 1 00 08 AM

notice how the "GG" shows up nowhere in the ZSAError. I'd personally expect GG to show up in the message property or at least data property

[issue] Having issues with FormData

I am loving the library so far ๐Ÿ”ฅ

One issue im having is when passing formdata from client side execute hook.

Here's how the hook looks like in the client:

 /** @actions */
  const { execute, isPending } =
    useServerAction(createAction, {
    });

My Submit Handler

const handleSubmit = (values) => {
    const formData = new FormData();

    formData.append('Name', "Hello");

    execute(formData);
  };
export const createAction = createServerAction()
  .input(typedAnySchema(), { type: 'formData' })
  .handler(async ({ input }) => {
    console.log(input, input instanceof FormData);
  });

I don't know why the instance of input comes false here, it should be true ?

Equivalent of trpc's `useUtils`

https://trpc.io/docs/client/react/useUtils

One of my favorite things about tRPC is that it automatically handles your query keys in a typesafe way:

const utils = trpc.useUtils();
utils.post.all.refetch();

This way you don't have to juggle keys manually.

It looks like that zsa has a few utilities to make query keys more typesafe, but it's still a lot of boilerplate that you have to constantly synchronize:

export const QueryKeyFactory = createServerActionsKeyFactory({ 
  getPosts: () => ["getPosts"],  
  getFriends: () => ["getFriends"],  
  getPostsAndFriends: () => ["getPosts", "getFriends"],  
  somethingElse: (id: string) => ["somethingElse", id],  
  getRandomNumber: () => ["getRandomNumber"], 
})  

So it would be nice if zsa can provide an equivalent to tRPC's useUtils that would absolve the need for all of this manual work.

Redirect in server action

I want to do i.e.

createServerAction()
.input(...)
.onSuccess(() => redirect("my/onsuccess/url")
.handler(() => ...);

Advanced React Hook Form Integration

One thing I've been struggling with it all of the boilerplate code involved with setting up a form with server actions (not specific to this library)

One awesome idea that I think might work well would be if ZSA could automatically coerce the form errors and ZSA errors into react hook form errors.

What the integration would do is:

  • Convert any input parse errors into form.setError(field, errorMessage)
  • Any ZSAErrors get converted into form.setError('root.serverError', errorMessage)
  • Pending state is available at form.formState.isSubmitting

API wise, I'm not too sure what that would look like. Maybe something that looks like this:

const [form, ...otherStuff] = useReactServerActionHookForm(action, additionalReactHookFormProps)

Upgrade from [email protected] to 0.3.3 keeps the action loading forever

What is the error?

Hi. I'm was working on a project using older versions and I moved to latest to have new ZSAError and the code that's working in older versions now leaves my UI loading

image
-    "zsa": "^0.2.3",
-    "zsa-react": "^0.1.3"
+    "zsa": "^0.3.3",
+    "zsa-react": "^0.1.4"

This is my action

async function checkUsername(username: string, user: SafeUser) {
  const foundUsers = await db
    .select({ id: users.id })
    .from(users)
    .where(and(eq(users.username, username), ne(users.id, user.id)))
    .limit(1)
  return foundUsers.length > 0
}

const input = z.object({
  name: z.string().min(3),
  username: z
    .string()
    .min(3, { message: 'Username must be at least 3 characters long.' })
    .max(20, { message: 'Username cannot be longer than 20 characters.' })
    .regex(/^[a-zA-Z0-9_\-.]*$/, {
      message:
        'Invalid characters in username. Only leters, numbers and "-" without spaces. No accents.',
    }),
})

type Input = z.infer<typeof input>

async function validateUniqueUsername({
  data,
  user,
}: {
  data: Input
  user: SafeUser
}) {
  const asyncInput = input.extend({
    username: input.shape.username.refine(async (username) => {
      return checkUsername(username, user)
    }, { message: 'Username is already taken.' }),
  })

  const uniqueError = await asyncInput.safeParseAsync(data)
  console.log("uniqueError", uniqueError)
  if (uniqueError.success) return

  const flattenedErrors = uniqueError.error.flatten()
  const formattedErrors = uniqueError.error.format()
  throw new ZSAError('INPUT_PARSE_ERROR', uniqueError.error, {
    inputParseErrors: {
      fieldErrors: flattenedErrors?.fieldErrors,
      formErrors: flattenedErrors?.formErrors,
      formattedErrors: formattedErrors,
    },
  })
}

export const onboarding = authProcedure
  .createServerAction()
  .input(input, { type: 'formData' })
  .output(input)
  .handler(async ({ input, ctx: { user } }) => {
    await validateUniqueUsername({ data: input, user })

    const result = await finishOnboarding({ user, data: input })
    const value = result.unwrap()

    // NOTE: This ends in JWT callback in auth/index.ts
    await updateSession({
      user: { name: value.name, username: value.username },
    })

    redirect('/')
  })

Experimental shapeError and OpenAPI

Hey,

Thanks for implementing the experimental global shapeError method, this is exactly what I needed. What's the best way to customise the OpenAPI error code (returns 500 by default). Should you use status if the property is in the error returned by shapeError?

{
    "type": "NotFound",
    "status": 404,
    "message": "Organization not found"
}

This way you could remove shapeError from the router possibly.

isSuccess is alway false when not returning data.

i am trying your example from the docs:

export const produceNewMessage = createServerAction()
  .input(
    z.object({
      name: z.string().min(5),
    }),
    {
      type: "formData", 
    }
  )
  .handler(async ({ input }) => {
    await new Promise((resolve) => setTimeout(resolve, 500))
    return "Hello, " + input.name
  })

it seems that when removing the return statement, the isSuccess is never true it is always false, is this the intended behavior ?

[issue] There is no typesafe relative to queryKeyFactory in setupServerActionHooks.useServerActionMutation.

I am using zsa-react-query on the client and while working with the library I needed to mutate the data, when I started inserting queryKey I noticed that the key types with createServerActionsKeyFactory are not supported. I looked in the source code and didn't see a type reference:

type TQueryKey = TFactory extends undefined
? Readonly<unknown[]>
: Readonly<ServerActionKeys<Exclude<TFactory, undefined>>>

type TUseMutation<
THandler extends TAnyZodSafeFunctionHandler,
TNeverThrow extends boolean = false,
> = typeof useMutation<
TNeverThrow extends false
? inferServerActionReturnData<THandler>
: inferServerActionReturnType<THandler>,
inferServerActionError<THandler>,
inferServerActionInput<THandler>
>

I think this would be useful, since mutation involves modifying and invalidating the react-query cache.
https://tanstack.com/query/v4/docs/framework/react/reference/useMutation

versions

"zsa": "0.5.0",
"zsa-react": "0.2.2",
"zsa-react-query": "0.2.0",

Feature Request: Ability to validate input on form change or on input change

This is more of a feature request than an issue. I used a hook from the project, kirimase, useValidatedForm, please find this code below:

"use client";

import { FormEvent, useState } from "react";
import { ZodSchema } from "zod";

type EntityZodErrors<T> = Partial<Record<keyof T, string[] | undefined>>;

export function useValidatedForm<Entity>(insertEntityZodSchema: ZodSchema) {
  const [errors, setErrors] = useState<EntityZodErrors<Entity> | null>(null);
  const hasErrors =
    errors !== null &&
    Object.values(errors).some((error) => error !== undefined);

  const handleChange = (event: FormEvent<HTMLFormElement>) => {
    const target = event.target as EventTarget;
    if (
      target instanceof HTMLInputElement ||
      target instanceof HTMLSelectElement ||
      target instanceof HTMLTextAreaElement
    ) {
      if (!(target instanceof HTMLInputElement && target.type === "submit")) {
        const field = target.name as keyof Entity;
        const result = insertEntityZodSchema.safeParse({
          [field]: target.value,
        });
        const fieldError = result.success
          ? undefined
          : result.error.flatten().fieldErrors[field];

        setErrors((prev) => ({
          ...prev,
          [field]: fieldError,
        }));
      }
    }
  };
  return { errors, setErrors, handleChange, hasErrors };
}

It basically receives a zod schema and returns errors and such utilities for interacting with the schema, but with this hook, I can basically add an onChange={handleChange} event to my form which then allows me to get errors as I type in the input. I think this will also be very useful for uses cases where the user is interested in such interactivity to handle errors on the fly as I also do whilst user types into the input.

Since zsa-react-zod functions like useServerAction has access to the schema, could it not also handle something like this and return a handFormChange and a handleInputChange function? In this case, the handleFormChange change could perform similar to this hook and validate each input on change and the handeInputChange could also be applied to individual input like handleInputChange('name') by accepting a field name or name input and return error only for that name input?

Maybe this could also help with this issue: #124 as the user could get the input validated before even submitting so will not get that user experience of the form flicking to show the new errors. Of course this does not mean that the form will not be validated on the server as in my use case with the useValidatedForm hook I shared here, I also use the same zod schema in my server action to validate the schema once more but it takes the pain of having the user wait to submit the form before they get errors and it can also cherry pick each input field so user could type in an input and get errors for that input only without running the validation for the other inputs.

Could this probably work best in the useActionState hook or perhaps a new hook altogether? I have only tried to use zsa-react for a day so I have not been able to deep dive into the package to see if this is already possible but at least from the docs, I don't see such a feature for live updates of errors. I will take the time this week to also browse the codebase to see if I could suggest a solution but I am adding it here just in case anyone has ideas on it or it's already in the works/working. Thanks.

Set application/json content-type

Hey, I've implemented the openapi example and it returns text/plain;charset=UTF-8 as the default content-type, should this default to application/json and how to overirde please?

Use different path param name

Would you consider changing the path param name in routes to use : instead of {} which is consistent with nextjs and other framework naming convention?

/api/:organizationId instead of /api/{organizationId}

A "use server" file can only export async functions, found object.

When using the procedure and server action below I get the following error:
Error: A "use server" file can only export async functions, found object.
I read this issue on next.js about the export for a 'use server' file must be a async function but after updating to the latest version of next.js I am still getting the error.

Versions

    "next": "14.2.4",
    "zsa": "^0.5.0",
    "zsa-react": "^0.2.2"
"use server";

import { notFound } from "next/navigation";
import { z } from "zod";
import authedProcedure from "../procedures/authed";

const productSchema = z.object({
  product_group: z.string(),
  url_slug: z.string(),
});

const getProductProcedure = authedProcedure
  .createServerAction()
  .input(z.object({ slug: z.string() }))
  .output(productSchema)
  .handler(async ({ input, ctx }) => {
    const response = await fetch(``).then(r => r.json());
    const product = await productSchema.parseAsync(response.results?.at(0));
    if (product) notFound();
    return product;
  });

export default getProductProcedure;
"use server";
import { createServerActionProcedure, ZSAError } from "zsa";
import { currentUser } from "@clerk/nextjs/server";

const authedProcedure = createServerActionProcedure().handler(async () => {
  try {
    const user = await currentUser();
    
return {
      user
    };
  } catch {
    throw new ZSAError("NOT_AUTHORIZED", "User is not authenticated");
  }
});

export default authedProcedure;

Run ESLint and Typecheck on CI and use pnpm

What?

Not really an issue. But a piece of feedback ๐Ÿ˜ƒ

CI lint & Typecheck

While working in this PR #124 I noticed CI is not running Linter or Typescript checks.

Is there any reason for that?

Also notice ESLint config is a big convoluted with double tsconfig.json and tsconfig.lint.json in all the packages. I think this could be simplified.

Use PNPM instead of NPM

I admit this one is not critical ๐Ÿ˜‚ . But I've working in all my monorepos with pnpm is so much better experience. Faster builds and better module resolution.

Custom Codes Types in "ZSAError"

Hi, I'm Using ZSAError() to Handle Errors. In the action I want to send error code "INSUFFICIENT_CREDITS" to the client. I know there are others way of achieving the same but was wondering if I can modify the types of the codes in ZSAError

[issue] createServerActionProcedure does't supported in useServerActionMutation

[zsa-react-query]

Error when trying to insert the result of the createServerActionProcedure function into useServerActionMutation.

error:

Argument of type 'CompleteProcedure<ZodObject<{ test: ZodString; }, "strip", ZodTypeAny, { test: string; }, { test: string; }>, THandlerFunc<ZodObject<{ test: ZodString; }, "strip", ZodTypeAny, { test: string; }, { test: string; }>, undefined, "ShapeErrorNotSet", Promise<void>, undefined, "json", true>, "ShapeErrorNotSet">' is not assignable to parameter of type 'TAnyZodSafeFunctionHandler'

import { z } from "zod";
import { createServerActionProcedure } from "zsa";

const TestAction = createServerActionProcedure()
  .input(z.object({ test: z.string() }))
  .handler(async () => {
    return;
  });

export const test = createServerActionProcedure(TestAction).handler(
  async () => {
    return;
  },
);
const mutation = useServerActionMutation(test) // Error

versions

"zsa": "0.5.0",
"zsa-react": "0.2.2",
"zsa-react-query": "0.2.0",

Facing trouble with FormData.

I would like to know how to deal with formData when using the useServerAction hook in the onSubmit of a form. I am sharing a common Zod schema between the client and server. When dealing with an array of objects, I stringify them and use formData.append. However, the createServerAction() .input(POST_SCHEMA, { type: 'formData' }) validates the input against the Post schema, causing a Zod error since the types are different. What is the best way to handle this?

Invalid opts, must originate from the server

I'm getting the error "Invalid opts, must originate from the server" when generating the specs from the server, most likely a change in my code but I can't figure out the reason. When would this error happen please?

Cannot access request when creating a procedure

I am checking if the request has come from a certain IP address. However, when creating a procedure, the request is always undefined even though it is typed to be available.

createServerActionProcedure().handler(
    async ({ request }) => {
        if (!request) {
            console.log('no request')
            throw new ZSAError('NOT_AUTHORIZED')
        }

Form Error: Invalid Server Actions request.

I am trying to submit a form but I am getting this error from nextjs Error: Invalid Server Actions request..

Screenshot 2024-07-03 17 12 02

I am on Next.JS 14.2.4.

This is my code for my action and form.

// page.tsx
import { TestForm } from './Form'

export default function Page() {
	return (
		<div>
			<TestForm />
		</div>
	)
}


// Form.tsx
'use client'

import { useServerAction } from 'zsa-react'

import { signInAction } from '@/lib/actions/auth'

export const TestForm = () => {
	const { isPending, executeFormAction, data, error } =
		useServerAction(signInAction)

	return (
		<form action={executeFormAction}>
			<input name="email" type="email" />
			<input name="password" type="password" />
			<button disabled={isPending}>Submit</button>
		</form>
	)
}

// lib/actions/auth.ts
'use server'

import z from 'zod'
import { createServerAction } from 'zsa'

export const signInAction = createServerAction()
	.input(
		z.object({
			email: z
				.string({ required_error: 'Email is required' })
				.email('Must be a valid email address'),
			password: z
				.string({ required_error: 'Password is required' })
				.min(8, 'Must be at least 8 characters')
				.max(64, 'Must not exceed 64 characters'),
		}),
		{ type: 'formData' },
	)
	.handler(async ({ input }) => {
		// Sleep for .5 seconds
		await new Promise((resolve) => setTimeout(resolve, 500))
		// Increment the input number by 1
		return 'I have not been implemented yet, please wait for a while'
	})

Also, I am seeing this in the browser dev tools when I check my form:
Screenshot 2024-07-03 17 12 23

How to FormData.getAll?

Hello! We just found this library a couple of weeks ago and have started trying to implement it into our application where we heavily use server actions. We ran into a case that the documentation does not mention so we are hoping there is an answer.

We have several cases in our server actions where we need to call FormData.getAll to get all inputs instead of FormData.get. How can we do this with this library?

For instance, we might have the code in the server action that looks like this:

        const result = zodSchema.safeParse({
		...Object.fromEntries(formData),
		files: formData.getAll('files'),
	});

Array as query argument

I have a zod array as an input property type, ie: input(z.object({ list: z.array(z.string()) })).

When using openapi with a GET, is there a way to pass list as an array via querystring? Something like list[]=foo&list[]=bar or list=foo&list=bar?

Thanks!

Best way of accessing invalid values with progressive enhancement in mind

In the context of using input with type="state" and invalid values with progressive enhancement in mind, is there a way of getting these (invalid) values to pass it for instance as defaultValues in RHF (in that way, the form can be filled with the values during server rendering even if javascript is disabled)?

actions.ts

"use server"

export const subscribeToNewsletterAction = createServerAction()
  .input(zNewsletterValues, {type: "state"})
  .handler(async ({input: {email}}) => subscribeToNewsletter(email))

newsletter-form.tsx

"use client"

export default function NewsletterForm() {
  const [[data, error], action, pending] = useActionState(subscribeToNewsletterAction, [null, null])

 const form = useForm<NewsletterValues>({
    mode: "onTouched",
    resolver: zodResolver(zNewsletterValues),
    defaultValues: ... // error?.values or similar
  })

For now, my workaround is to create an extra function like the one in custom state when I can access formData directly and pass it through:

actions.ts

"use server"

const subscribeToNewsletterZSA = createServerAction()
  .input(zNewsletterValues, {type: "state"})
  .handler(async ({input: {email}}) => subscribeToNewsletter(email))

export const subscribeToNewsletterAction = async (
  _prev: NewsletterState,
  formData: FormData
): Promise<NewsletterState> => {
  const [data, error] = await subscribeToNewsletterZSA(formData)
  if (data) return [data, null] 
  const values = Object.fromEntries(formData.entries()) as NewsletterValues
  return [null, {...error, values}]
}

Should zod and react be used as peerDependencies?

We were working in monorepo and ran into the multiversion problem when trying next-rc

next-rc package.json:

"dependencies": {
  "next": "^15.0.0-rc.0",
  "react": "^19.0.0-rc",
  "react-dom": "^19.0.0-rc",
  "zod": "^3.23.8",
  "zsa": "^0.4.0",
  "zsa-react": "^0.1.9"
}

run yarn list:

yarn list zod:
โ”œโ”€ [email protected]
โ”‚  โ””โ”€ [email protected]
โ”œโ”€ [email protected] // This is the version the other local packages are using
โ”œโ”€ [email protected]
โ”‚  โ””โ”€ [email protected]
โ””โ”€ [email protected]
   โ””โ”€ [email protected]

yarn list react:
โ”œโ”€ [email protected]
โ”‚  โ””โ”€ [email protected]
โ””โ”€ [email protected]
   โ””โ”€ [email protected]

Support input formData entries with consistent field name

Cases where a formData is passed with multiple entries with the same field name are not handled, so if we pass a formData to the server action that had several formData.append("name") on the client we will only get the last entry. Libs like zod-form-data handle this case automatically by turning the filed into an array.

I can provide more info if necessary, and If this is already possible, please correct me!

[docs] add infer types

Add docs for the following helper types

  • inferServerActionReturnData
  • inferServerActionReturnType
  • inferServerActionInput

useServerAction and useTransition #zsa-react

Issue: execute async function never fulfilled when it invoked inside startTransition cb.

In this example I log all statuses into console.
When i invoked execute fn inside startTransition cb isPending (from useTransition) always true and loader never ending...

~console.log:
status idle
START
status pending
status pending

but when i replacing execute with executeFormAction || or use action directly โ€“ everything works great...

~console.log:
status idle
START
status pending
status pending
status success
SUCCESS
FINISHED

I need useTransition to handle redirecting process router.push(/blog/${post.slug});...

// Example 
export const EditPostForm: RC<IProps> = (props) => {
  const router = useRouter();
  const [isPending, startTransition] = useTransition();
  const { execute, status } = useServerAction(updatePost, {
    onStart: () => {
      console.log('START');
    },
    onSuccess: () => {
      console.log('SUCCESS');
    },
    onError: (error) => {
      console.log('ERROR', error);
    },
    onFinish: () => {
      console.log('FINISHED');
    },
  });

  console.log('status', status);

  return (
    <PostForm
      loading={isPending}
      onSubmit={async (values) => {
        startTransition(async () => {
          const [post] = await execute({ id, values });
          if (!post) return;
          router.push(`/blog/${post.slug}`);
        });
      }}
    />
  );
};

[docs] possible alt conventions

In our current docs it says to do:

createServerAction().input(...)

or

myProcedure.createServerAction().input(...)

But the problem is could be better DX to not have to write createServerAction everytime and just do something like this

const publicAction = createServerAction()
const authAction = createServerActionProcedure()...createServerAction()

const action1 = publicAction.input(...)
const action2 = authAction.input(...)

Progressive enhancement

Hello,
I'm trying to use zsa on a form with javascript disabled but it does not seem to work. Is progressive enhancement manageable with zsa?

Unable to use useServerAction on chained procedure

I am not sure if this is something not doable, thus a request, or something that I am doing wrong.
First, I create an server action using a procedure that requires the admin role
I end the function with .createServerAction();

export const createProductAction = createServerActionProcedure(adminProcedure)
  .input(createProductSchema)
  .handler(async ({ input }) => {
    try {
      const { error } = await db.from("products").insert([
        {
          name: input.name,
        },
      ]);
    } catch (error) {
      throw new Error("An error as occured");
    }
  })
  .createServerAction();

Then, in my client form CreateProduct, I want to use the useServerAction hook and deconstruct the props to handle the states.

But I have a TS error on createProductAction, in that line:
const {isPending,data,execute,isError}=useServerAction(createProductAction) and can't actually use that file.

What am I doing wrong?
I am so sorry if this is not the right place for this request.

Keep errors while submiting to the server

What?

Hi, I'm using useServerAction to save some data into my server. It's working fine but I see a glitch while isPending

The errors in the server are reset and the UI jumbs.

This is my form:

  const { data, executeFormAction, isPending, error, ...rest } = useServerAction(
    onboarding,
    {
      initialData: { name, username },
    },
  )
  const errorData = error?.data
  const fieldErrors = error?.fieldErrors
  return (
      <Input
        label='Your username'
        type='text'
        name='username'
        errors={fieldErrors?.username}
        defaultValue={data?.username ?? errorData?.username}
        onChange={(e) => setUserName(e.target.value)}
        placeholder='Pick a username'
        description={<UsernameDescription username={currentUsername} />}
      />
  )

This is my action:

export const onboarding = authProcedure
  .createServerAction()
  .input(input, { type: 'formData' })
  .output(input)
  .experimental_shapeError(({ err, typedData }) => {
    if (err instanceof ZSAError && err.code !== 'INPUT_PARSE_ERROR') {
      return {
        code: err.code,
        message: err.message,
      }
    }

    return {
      data: typedData?.inputRaw,
      fieldErrors: typedData?.inputParseErrors?.formattedErrors,
    }
  })
  .handler(async ({ input, ctx: { user } }) => {
    await validateUniqueUsername({ data: input, user })

    const result = await finishOnboarding({ user, data: input })
    const value = result.unwrap()
    await updateSession({
      user: { name: value.name, username: value.username },
    })

    redirect('/')
  })

This is my UI. Check the disappearing red errors when submitting

Screen.Recording.2024-06-15.at.16.09.36.mov

Additional parameters in addition to formData

I have defined a Zod Schema and the type: formData, but i need to add additional parameters to the server action. I used Bind on the method before transitioning to ZSA, but it does not seem to work. I can't figure out if it's supported or not, do we have a way to do add additional parameters ?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.