Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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

issue + feature request: defaultValue of type Date is not applied to<input type=date> - with register API #11798

Closed
1 task done
tresorama opened this issue Apr 19, 2024 · 4 comments

Comments

@tresorama
Copy link

tresorama commented Apr 19, 2024

What

Doing this...

const Component = () => {
  const { register } = useForm({
    defaultValue: {
      day: new Date("2024-12-31"),
    }
  })

  return <input type="date" {...register('day', {valueAsDate: true })} />
}

... result in this
Schermata 2024-04-22 alle 18 43 27
an "input" in blank/empty state, instead of showing an already selected date.
This means that the defaultValue defined in useForm is not respected.

Even if we define defaultValue as input props the outcome is the same

  return <input type="date" defaultValue="2024-12-31" {...register('day', {valueAsDate: true })} />
Schermata 2024-04-22 alle 18 43 27

Version Number

7.51.3

Codesandbox/Expo snack

https://stackblitz.com/edit/vitejs-vite-v95kgv?file=src%2Fsections%2Foriginal-issue.tsx

Steps to reproduce

  1. Open sandbox
  2. See the pink form field (key:day) that looks as "empty" even if default value is present
  3. Look also that watch show that default value is memory state is correct

Note 1

It seems that the culprit is the ref returned from register that is some way overwrite defaultValue.
Look for day3 input where i omitted passing ref and it show the default value correctly.
Because there is no ref, it's not a valid workaround. react hooks form can no longer track field value changes.

Note 2

A workaround is to use useEffect and update the value of the field after first render.
Look for day2 input where I did this. It show the default value correctly.
But it's "hacky" and inconvenient.

Expected behaviour

This code should let the browser input field show that a date is already selected.
Now it show a blank/empty state.

<input 
  type="date" 
  defaultValue="2024-04-04"
  {...register('day', {valueAsDate:true})}
/>

Tested only in Chrome.

What browsers are you seeing the problem on?

Chrome

Relevant log output

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct
@tresorama

This comment was marked as duplicate.

@tresorama tresorama changed the title issue: defaultValue (Date) and <input type=date> not working (with register) issue: defaultValue of type Date is not applied to<input type=date> - with register API Apr 22, 2024
@tresorama
Copy link
Author

tresorama commented Apr 22, 2024

To give better context to this issue...

Goal

Let's say the you want a form with an input the let you select a date, but you want to set a default date.
Doing that when the user open the form a date is already in place (for example the "today" date)

This does not work

const Component = () => {
  const { register } = useForm({
    defaultValue: {
      day: new Date("2024-12-31"),
    }
  })

  return <input type="date" {...register('day', {valueAsDate: true })} />
}

Output:
Schermata 2024-04-22 alle 18 43 27

This works - use Controller API

If you instead switch from register API to <Controller> API you can:

  • edit how reach-hook-form => set value to => html input element
  • edit how react-hook-form => get value from the onChange event and set it to => react-hook-form state
const Component = () => {
  const { register } = useForm({
    defaultValue: {
      day: new Date("2024-12-31"),
    }
  })

  return (
  <Controller
    control={control}
    name="day"
    render={({field}) => (
      <input
        {...field}
        type="date"
        value={field.value ? field.value.toISOString().substring(0,10) : undefined}
        onChange={( {target:{value}} ) => field.onChange( value === '' ? undefined : new Date(value) ) }
      />
    )}
  />
  )
}

Consideration

But this means that you cannot choose which api to use (register or Controller), but as soon as you need a date input you MUST use the controller api, which is less performant

Open Question

Why not exposed in the registr api a similar way to manipulate the value between react-hook-form state and html input state (DOM state) ??
Something like

<input 
  type="date" 
  {...register("day", {
    // input value => react-hook-form state
    getValueFromInputAs:  value => value === '' ? undefined : new Date(value), 
    // react-hook-form-state => input value
    setValueToInputAs: value => value ? value.toISOString().substring(0,10) : undefined, 
  })}
/>

This works - use string instead of Date and accept a complex validation schema

Because this doesn't work ...

const schema = z.object({
  day: z.date(),
});
const Component = () => {
  const { register } = useForm<z.input<typeof schema>>({
    defaultValue: {
      day: new Date("2024-12-31"),
    }
  })
  // ...
}

... you can switch the field type from Date to string like ...

const { register } = useForm<z.input<typeof schema>>({
    defaultValue: {
      day: "2024-12-31", // now it's a string
    }
  })

... but the validation schema will complain (showing Typescript errors). You can fix this by adding more complexity to the schema, with downside of coupling the schema with HTML input behavior.

const schema = z.object({
  // 1. validate html input values
  day: z.string().min(1, "Required field"),
}).transform(input => ({
 // 2. If step 1 is valid, cast/coerce data types for the step 3
  day: new Date(input.day),
}).pipe(z.object({
  // 3. validate data
  day: z.date(),
});

... and updating also useForm generic types to have correct inference inside handleSubmit

type SchemaInput = z.input<typeof schema>;
type SchemaOutput = z.output<typeof schema>;

const { register, handleSubmit } = useForm<SchemaInput, any, SchemaOutput>({
    defaultValue: {
      day: "2024-12-31", // now it's a string
    }
  })

<form onSubmit={handleSubmit((formValues) => {
  // here formValues has SchemaOutput types and not SchemaInput
  // so "formValues.day" is of type Date, not string 
}}

@tresorama
Copy link
Author

tresorama commented Apr 22, 2024

Feature request

Why not exposeing in the register api a way to manipulate the value between react-hook-form state and DOM html input state ??
This will give "parity" with the Controller API.

Something like

<input 
  type="date" 
  {...register("day", {
    // input value => react-hook-form state
    getValueFromInputAs:  value => value === '' ? undefined : new Date(value), 
    // react-hook-form-state => input value
    setValueToInputAs: value => value ? value.toISOString().substring(0,10) : undefined, 
  })}
/>

@tresorama tresorama changed the title issue: defaultValue of type Date is not applied to<input type=date> - with register API issue + feature request: defaultValue of type Date is not applied to<input type=date> - with register API Apr 22, 2024
@kujohn
Copy link

kujohn commented Apr 29, 2024

I am having this issue as well. defaultValue doesn't work at all with type=date inputs.

@react-hook-form react-hook-form locked and limited conversation to collaborators May 5, 2024
@bluebill1049 bluebill1049 converted this issue into discussion #11860 May 5, 2024

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants