modular-forms icon indicating copy to clipboard operation
modular-forms copied to clipboard

[Qwik] Form state is reset after action returns an error

Open DustinJSilk opened this issue 11 months ago • 1 comments

Hi fabian,

Sorry there's no way to reproduce this issue. I can't reproduce it outside of my production code yet. I'll try explain the problem and share some code that is similar but doesn't suffer from the same problem.. maybe you have an idea?

Latest qwik (1.4.5) and latest modular forms

Problem

If my formAction$ returns a server error the routeLoader$ refetches, the component rerenders, and all form state is reset. The error message that was returned then doesn't show and all values are reset. If I try again, it then works.

Interesting bits:

  • The problem doesn't exist if I refresh the page on the form route, it only happens if I refresh the page on another route and navigate to the form using a <Link/> and then submit it.
  • It only happens the first time I submit the form after navigating to the page. All attempts thereafter keep the form state and it works fine.
  • If I use a field transform prop with toCustom$, the whole component rerenders with each keydown on the input, the problem is still the exact same with or without it. So can't be a case of a rerendering issue?

Example

This example doesn't have the same problem, but the code is quite similar but simplified.

import { Slot, component$, useComputed$, useSignal } from "@builder.io/qwik";
import { routeLoader$, z } from "@builder.io/qwik-city";
import { formAction$, useForm, zodForm$ } from "@modular-forms/qwik";

const schema = z.object({ value: z.number() });
type Form = z.infer<typeof schema>;

const useFormAction = formAction$<Form>(async () => {
  return {
    status: "error",
    message: "Some error message",
  };
}, zodForm$(schema));

export const useSomeData = routeLoader$(async () => {
  return {
    value: 100,
  };
});

export default component$(() => {
  const data = useSomeData();

  return (
    <div>
      <Toggle>
        <MyForm data={data.value}></MyForm>
      </Toggle>
    </div>
  );
});

const MyForm = component$((props: { data: { value: number } }) => {
  const initial = useComputed$(() => ({
    value: props.data.value * 2,
  }));

  const [form, { Form, Field }] = useForm<Form>({
    loader: initial,
    action: useFormAction(),
    validate: zodForm$(() => schema),
    validateOn: "submit",
  });

  return (
    <div>
      {initial.value.value}
      <Form>
        <Field name="value" type="number">
          {(field, p) => (
            <>
              <input {...p} type="number" value={field.value} />
              {field.error && <span>{field.error}</span>}
            </>
          )}
        </Field>

        {!form.submitting && form.response.status === "error" && (
          <p>{form.response.message ?? "Unknown error"}</p>
        )}

        <div>{!form.submitting && <button>Submit</button>}</div>
      </Form>
    </div>
  );
});

const Toggle = component$(() => {
  const show = useSignal(false);
  return (
    <div>
      <button onClick$={() => (show.value = !show.value)}>Toggle</button>

      {show.value && <Slot />}
    </div>
  );
});

Thanks for all the amazing work on this! I'll try reproduce the problem eventually.

DustinJSilk avatar Mar 01 '24 12:03 DustinJSilk

For the transform problem this might help. For the other problem, I don't know what to do. Seems more like a Qwik problem than a Modular Forms problem.

fabian-hiller avatar Mar 01 '24 18:03 fabian-hiller