I have a weird issue with radio inputs. I have form in Next 15 (React 19) with a controlled state. The problem I'm having is that when I submit my form, the checked radio input is suddenly appearing unchecked. But the React state shows it still as being checked.
I have seen that with React 19 forms are auto cleared when submitted, so I changed from an uncontrolled state to a controlled one. As I don't want to clear my form if the action returns any validation errors. This works as I'd expect for text inputs.
Steps:
formValues.searchType = 1
"use client"
...
const defaultState = {
errors: [],
fieldValues: {
searchType: "",
}
};
export default function SomeForm() {
const [actionState, formAction, isPending] = useActionState(importedAction, defaultState);
const [formValues, setFormValues] = useState(defaultState.fieldValues);
console.log(formValues.searchType);
const handleRadioOption = (e: ChangeEvent<HTMLInputElement>) => {
setFormValues({
...defaultState.fieldValues,
searchType: e.currentTarget.value
});
}
return (
<Form action={formAction}>
<fieldset>
<legend>Select an option</legend>
<input
type="radio"
id="option1-radio"
label="Search by 1"
aria-controls="conditional-1"
name="searchType"
value="1"
onChange={handleRadioOption}
checked={formValues.searchType === "1"}
/>
{formValues.searchType === "1" && <div id="conditional-1">conditional section 1</div>}
<input
type="radio"
id="option2-radio"
label="Search by 2"
aria-controls="conditional-2"
name="searchType"
value="2"
onChange={handleRadioOption}
checked={formValues.searchType === "2"}
/>
{formValues.searchType === "2" && <div id="conditional-2">conditional section 2 </div>}
</fieldset>
<button disabled={isPending}>Continue</Button>
</Form>
);
}
I expect the radio input to render as checked or unchecked based on the checked
prop, but it is always appearing unchecked after submitting the form.
I have tried using uncontrolled state with defaultValue
, but I need to also control the rendering of a conditional section, so that's not an option.
I have tried a normal <form/>
element as well as the Next <Form/>
but both have this issue
Added a codesandbox here showing the issue: https://codesandbox.io/p/devbox/cocky-shannon-forked-8qlwzy?file=%2Fapp%2Fform%2Fcomponent.tsx%3A11%2C15&workspaceId=ws_YJunuyU6avBk8pMTbNjaGv
This is a bug with the latest version of React 19. There is an issue raised here: https://github.com/facebook/react/issues/31695
I have a workaround, which is to add a onSubmit
handler to the form, prevent the default behaviour, and manually call the action inside startTransition.
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(formRef.current);
startTransition(() => {
formAction(formData);
});
};