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 GitHub Comments
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
defaultFormat
instead offormat
?Okay, after a bit of digging, this is a bit more subtle than I originally thought. The behavior of
allowNull
hasn’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
format
if 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 howField
orders 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 doescreateElement
and 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
allowNull
uses==
to catchundefined
as well. IMO, there’s usually not a useful distinction betweenundefined
andnull
anyways, and I take a cue from Elm and use aMaybe
type 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
initialValue
toField
, passinitialValues
toForm
, 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
initialValue
will 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.