Controlled/Uncontrolled warning when using formatOnBlur
See original GitHub issueAre you submitting a bug report or a feature request?
Bug report. (I think!)
What is the current behavior?
I use format and formatOnBlur to trim whitespace from a field when the user leaves it. I know that I must not return undefined in format.
I get this warning when typing in the field:
Warning: A component is changing an uncontrolled input of type undefined to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.
What is the expected behavior?
No warnings!
Note that if I remove formatOnBlur the warnings do not appear.
Sandbox Link
https://codesandbox.io/embed/nervous-williams-k1ldk
What’s your environment?
react-final-form: 6.1.0 final-form: 4.14.1 browser: Tried Chrome and Firefox
Other information
The code from the sandbox for convenience:
<Form
onSubmit={values => {
console.log("SUBMIT", values);
}}
>
{({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<Field name="name" format={(value = "") => value.trim()} formatOnBlur>
{({ input }) => {
return <input {...input} />;
}}
</Field>
</form>
)}
</Form>;
Also, thank you for this fantastic form solution! 💯
Issue Analytics
- State:
- Created 4 years ago
- Reactions:12
- Comments:7

Top Related StackOverflow Question
This behavior seems broken. It makes it impossible (unless you’re willing to tolerate the controlled/uncontrolled warning from React) to use
{...input}to spread field props into an underlying<input>element. Instead, you need to do this:I’m also confused why the code that @csantos1113 excerpted above. Why use
defaultFormatinstead offormat?Okay, after a bit of digging, this is a bit more subtle than I originally thought. The behavior of
allowNullhasn’t changed. In both 4.x and newer versions, it uses strict equality. I was thinking it was a change from==to===so it would catch undefined values before but no longer does. Instead the change is even more confusing:4.1.0
Current
That’s weird. So previously, you have to opt out of
formatif you don’t want it to run (b/c it’s defined indefaultProps. And withformatOnBlur, it always uses the default format in render (but the passed in format on blur). That’s a wow. And now, you can’t opt out offormat(probably fine, though).Now, it only runs the default format if you pass
component="input", which I verified will remove the error. However, this leads to even more potential confusion, b/c of howFieldorders its render checks:So, if you pass a function as child to
<Field component="input" format={format} formatOnBlur>, you get the full behavior, and you won’t get an uncontrolled error. But if you pass a render prop, you’ll get errors b/c as you can see it just doescreateElementand spreads the props down (which will get spread onto theinput, resulting in errors).All of this is a bit confusing. Best case for me is
allowNulluses==to catchundefinedas well. IMO, there’s usually not a useful distinction betweenundefinedandnullanyways, and I take a cue from Elm and use aMaybetype for anything where these distinctions aren’t meaningful (e.g.type Maybe<T> = T | null | undefined;).In the meantime, there’s a few things we can do. Pass
initialValuetoField, passinitialValuestoForm, create a helper that normalizesinput.value, etc.Hopefully this can be meaningfully addressed, b/c it leaves the API in a strange place as is.
Update edit: This is even worse than I described. While
initialValuewill fix focus and blur errors, there are still errors thrown if you type something then delete the character, which makes the input value go from""toundefined. The only way to fix this is to catch it in the input itself:<input {...input} value={input.value || ''}/>. Not lovely.