edmundhung / conform

A type-safe form validation library utilizing web fundamentals to progressively enhance HTML Forms with full support for server frameworks like Remix and Next.js.
https://conform.guide
MIT License
2.03k stars 105 forks source link

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

Open Merott opened 2 months ago

Merott commented 2 months ago

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:

https://github.com/user-attachments/assets/deceff2f-908e-467d-bdaa-4eb59b9fa17b

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

https://github.com/user-attachments/assets/fd372afe-0ba2-4bf4-abca-218e32fc812f

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 ```tsx // 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 = (
{!insideSpecialComponent && ( This form is rendered directly on the page. It works normally—no bugs! )} Enter a greeting: "{fields.greeting.value}" ); const content = insideSpecialComponent ? ( This form is placed inside our "SpecialComponent", which only renders its children on subsequent renders (typical pattern for client-only components). 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. This only happens after you type the first character. The input field will not reset again if you continue typing. {formNode} ) : ( formNode ); return ( { } {content} ); } 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 commented 2 months ago

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.