Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Form input resets unexpectedly when rendered inside a client-only component #777

Open
Merott opened this issue Sep 19, 2024 · 1 comment

Comments

@Merott
Copy link

Merott commented Sep 19, 2024

Describe the bug and the expected behaviour

When a form created with @conform-to/react is placed inside a component that only renders its children on the client-side (a common pattern for client-only components), the form's input field unexpectedly resets to its initial value after the first character is typed. This only occurs on the first input; subsequent inputs behave normally.

Expected behaviour: The form's input field should maintain the typed character and not reset to its initial value, regardless of whether it's inside a client-only component or not.

Conform version

v1.2.1

Steps to Reproduce the Bug or Issue

  1. Open the CodeSandbox demo
  2. Click the "Activate issue demo" button to render the form inside the SpecialComponent.
  3. Try typing any character into the input field.
  4. Observe that the input field resets to its initial value of '______', but the form state (displayed below the input) shows the character you just typed.
  5. Continue typing and notice that the issue doesn't occur again.

To compare with normal behaviour, refresh the page and type in the input field before clicking the "Activate issue demo" button.

What browsers are you seeing the problem on?

Chrome, Firefox, Safari

Screenshots or Videos

Works normally when the form is rendered immediately on the page:

CleanShot.2024-09-19.at.17.19.59.mp4

But when rendered as a "client-side" component on the second render, it resets:

CleanShot.2024-09-19.at.17.25.47.mp4

Additional context

The SpecialComponent in the Codesandbox demo is just a simplified version of client-only rendering, and quite common in React applications with SSR.

For reference, here's the full code from the demo in Codesandbox
// App.tsx

import { getInputProps, getFormProps, useForm } from "@conform-to/react";
import { Text, TextField, Flex, Theme, Button } from "@radix-ui/themes";
import { useEffect, useState } from "react";

export function App() {
  const [insideSpecialComponent, setInsideSpecialComponent] = useState(false);

  const [form, fields] = useForm({
    onSubmit: (e) => e.preventDefault(),
    defaultValue: { greeting: "______" },
  });

  const formNode = (
    <form {...getFormProps(form)}>
      {!insideSpecialComponent && (
        <Flex direction="column" mb="5">
          <Text>This form is rendered directly on the page.</Text>
          <Text weight="bold">It works normally—no bugs!</Text>
        </Flex>
      )}
      <Text as="label" htmlFor={fields.greeting.id} weight="bold">
        Enter a greeting:
      </Text>
      <TextField.Root
        {...getInputProps(fields.greeting, { type: "text" })}
        mt="2"
        autoFocus
        placeholder="Hi!"
      />
      <Flex mt="4">
        <Text weight="bold">"{fields.greeting.value}"</Text>
      </Flex>
    </form>
  );

  const content = insideSpecialComponent ? (
    <SpecialComponent>
      <Flex direction="column" mb="5" gap="2">
        <Text>
          This form is placed inside our "SpecialComponent", which only renders
          its children on subsequent renders (typical pattern for client-only
          components).
        </Text>
        <Text>
          Try inputting any text. When you type the first character, the input
          field will reset to its initial value of '______'... The field value,
          displayed below the input, will have the character you just typed.
        </Text>
        <Text>
          This only happens after you type the first character. The input field
          will not reset again if you continue typing.
        </Text>
        {formNode}
      </Flex>
    </SpecialComponent>
  ) : (
    formNode
  );

  return (
    <Theme>
      {
        <Button
          mb="4"
          onClick={() => setInsideSpecialComponent(true)}
          disabled={insideSpecialComponent || form.dirty}
        >
          Activate issue demo (must do this before you type)
        </Button>
      }

      {content}
    </Theme>
  );
}

function SpecialComponent({ children }: { children: React.ReactNode }) {
  const [isClient, setClient] = useState(false);

  useEffect(() => {
    setClient(true);
  }, []);

  // For whatever reason, this special component will only render itself on the client...

  return isClient ? children : null;
}
@edmundhung
Copy link
Owner

Hey @Merott, I'm sorry for the issues with the release, and thank you so much for the CodeSandbox—it helped uncover some design flaws in the current solution. I believe it's best to revert the relevant changes (#778), and I will publish a patch shortly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants