remix-hook-form icon indicating copy to clipboard operation
remix-hook-form copied to clipboard

Cannot clear custom errors when returned by the server

Open fenos opened this issue 2 years ago • 8 comments
trafficstars

Hello, thanks for the library! Got a small issue

When i'm returning a custom error from the action as following:

export async function action({request, context, params}: ActionArgs) {

    const {
        data,
        errors,
        receivedValues ,
      } = await getValidatedFormData(request, zodResolver(createSpaceInput))

      try {
        const result = context.api.onboarding.setUpSpace.mutate({
            name: data.name
          })
      
        return json({ space: result })
      } catch(e) {
        return json({ 
            errors: {
                root: {
                    message: e.message
                }
            } 
        })
      }
  }

The error can be displayed just fine. However, when i try to clear the error for example onChange it simply doesn't clear.

 const form = useRemixForm({
    mode: "onSubmit",
    defaultValues: {
      name: "",
    },
    resolver: zodResolver(formSchema),
  });

<RemixForm onSubmit={form.handleSubmit} method={"POST"} onChange={() => {
              console.log('form on change', form.formState.errors)
              form.clearErrors('root')

            }}>

I have also noticed that when I return an error which has the same name as one of the fields:

export async function action({request, context, params}: ActionArgs) {
     ... // rest of the function

        return json({ 
            errors: {
                name: {
                    message: 'some backend error coming from external api'
                }
            } 
        })
  }

It behaves the same way, as in the message is shown just fine, but it can't be cleared up

Any tips?

fenos avatar Jul 15 '23 14:07 fenos

@fenos Hello! The issue you're facing is the fact that when you return the remix errors from the backend they are consumed internally by the hook until they are cleared in the useActionData (eg you submit again). This means that there is no way to remove the errors via form.clearErrors, but this should not indeed be the case but should be cleared. I will make a patch for this when I get the time and this behavior should remove it so this is definitely not the way it should work. Until then I could recommend that you check if the error that comes from the hook is the same as the be one and clear it like that by using useActionData and handling the errors that way

AlemTuzlak avatar Jul 16 '23 08:07 AlemTuzlak

Any progress on this? I have just come across the same issue. If you have a recommend solution I can implement it for you.

Centerworx avatar Aug 06 '23 03:08 Centerworx

Any progress on this? I have just come across the same issue. If you have a recommend solution I can implement it for you.

@Centerworx i think the solution is to have a simple setState that is set to true on submit and false on reset and depending on that it consumes the be errors

AlemTuzlak avatar Aug 06 '23 09:08 AlemTuzlak

@AlemTuzlak Working on it. There is an issue around useActionData return the value before submit true is set. Even though it is set to true before calling submit handler. But this may just be test implementation issue.

Centerworx avatar Aug 06 '23 22:08 Centerworx

@AlemTuzlak I have the fix done and tested. I'm having an issue pushing it. Problem with my git.

Centerworx avatar Aug 07 '23 04:08 Centerworx

Hi, first of all, thanks to @AlemTuzlak for making remix-hook-form. I used usePrevious in the project to get the difference of serverErrors and then merged it, which temporarily solved the problem.

  const errors = data?.errors || emptyErrors
  const [serverErrors, setServerErrors] = React.useState(() => errors)
  const previousServerErrors = usePrevious(serverErrors)

  const formErrors = React.useMemo(() => {
    const keys = Object.keys(serverErrors)
    const prevKeys = previousServerErrors ? Object.keys(previousServerErrors) : []
    const deletedErrorKeys = prevKeys.filter((key) => !keys.includes(key))

    const localErrors = methods.formState.errors as FieldErrors<Output>
    const mergedErrors = mergeErrors<Output>(localErrors, serverErrors, validKeys)

    for (const key of deletedErrorKeys) {
      delete mergedErrors[key]
    }

    return mergedErrors
  }, [methods.formState.errors, previousServerErrors, serverErrors, validKeys])

  React.useEffect(() => {
    if (isSubmitting) {
      setServerErrors(emptyErrors)
    } else {
      // Only update the server errors if the form is not submitting
      setServerErrors(errors)
    }
  }, [errors, isSubmitting])

xesrevinu avatar Aug 24 '23 09:08 xesrevinu

Is there any progress on this? I just run into the same issue.

piotrkulpinski avatar Nov 10 '23 20:11 piotrkulpinski

So this might be potentially fixed with the fact that I moved to using the react-hook-form native "errors" prop, can anyone confirm?

AlemTuzlak avatar Jun 20 '24 09:06 AlemTuzlak