conform icon indicating copy to clipboard operation
conform copied to clipboard

Unexpected navigation can occur with Remix useFetcher and resetForm: true

Open aust1nz opened this issue 6 months ago • 3 comments

Describe the bug and the expected behavior

I've noticed some funny interplay between using Remix's useFetcher and conform's resetForm option, specifically when the useFetcher hits an action from another route. I've created a sandbox that shows this issue here:

https://codesandbox.io/p/devbox/interesting-hamilton-xt5pwn

Specifically, if the user fills out a name and hits enter to submit the form, they're brought to the JSON response that the action returns. Interestingly, if the user clicks the submit button, the form works as expected.

Conform version

1.1.5

Steps to Reproduce the Bug or Issue

https://codesandbox.io/p/devbox/interesting-hamilton-xt5pwn

Alternatively, create a basic Remix app with zod, @conform-to/react, and @conform-to/zod installed.

Update routes/index.tsx

import { getFormProps, getInputProps, useForm } from "@conform-to/react";
import { getZodConstraint, parseWithZod } from "@conform-to/zod";
import { json, type MetaFunction } from "@remix-run/node";
import { useActionData, useLoaderData } from "@remix-run/react";
import { useFetcher } from "react-router-dom";
import { z } from "zod";
import { action as createUserAction } from "./create-user";

export const users = [{ name: "Austin" }];
const schema = z.object({
  name: z.string().min(2),
});

export const meta: MetaFunction = () => {
  return [
    { title: "New Remix App" },
    { name: "description", content: "Welcome to Remix!" },
  ];
};

export async function loader() {
  return json(users);
}

export default function Index() {
  const users = useLoaderData<typeof loader>();
  const fetcher = useFetcher<typeof createUserAction>();
  const lastResult = useActionData<typeof createUserAction>();
  const [form, fields] = useForm({
    lastResult: fetcher.state === "idle" ? fetcher?.data : null,
    constraint: getZodConstraint(schema),
    shouldValidate: "onBlur",
    shouldRevalidate: "onInput",
    onValidate({ formData }) {
      return parseWithZod(formData, { schema });
    },
    defaultValue: {
      name: "",
    },
  });

  return (
    <div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.8" }}>
      <fetcher.Form method="post" action="/create-user" {...getFormProps(form)}>
        <p>
          This example uses a Remix fetcher to submit data to another route. If
          you click "Submit", you'll see things work as expected. The route is
          accessed, and returns a submission.result that clears the input.
        </p>
        <p>
          But if you fill in a value in the input and hit Enter, you're
          unexpectedly navigated to the remote route, which returns JSON.
        </p>
        <label>User Name</label>
        <br />
        <input {...getInputProps(fields.name, { type: "text" })} />
        {fields.name.errors && <div>{fields.name.errors[0]}</div>}
        <br /> <input type="submit" value="Submit" />
      </fetcher.Form>
      {users.map((user, index) => (
        <div key={index}>{user.name}</div>
      ))}
    </div>
  );
}

Create routes/create-user.tsx

import { parseWithZod } from "@conform-to/zod";
import { ActionFunctionArgs } from "@remix-run/node";
import { users } from "./_index";
import { z } from "zod";

const schema = z.object({
  name: z.string().min(4),
});

export async function action({ request }: ActionFunctionArgs) {
  const formData = await request.formData();
  const submission = parseWithZod(formData, { schema });
  if (submission.status !== "success") {
    return submission.reply();
  }

  users.push({
    name: submission.value.name,
  });
  return submission.reply({
    resetForm: true,
  });
}

What browsers are you seeing the problem on?

Chrome

Screenshots or Videos

No response

Additional context

I suppose it's possible this is a Remix issue, but seems to specifically happen when the submission.response({ resetForm: true }) option is configured.

aust1nz avatar Aug 03 '24 22:08 aust1nz