Katharina Schreiber
Katharina Schreiber

Reputation: 1371

React-Hook-Form Controller ignoring useEffect

I have two components, where selectBirthMonth depends on SelectBirthYear. Which is why I use useEffect, to watch the selectedYear change, so that I can change selectedMonth in case it is needed.

code sandbox

So in controller context my components look the following

<Controller
   control={control}
   name="selectedBirthYear"
   defaultValue={years[0]}
   render={({ field }) => (
     <SelectBirthYear
       field={field}
       years={years}
       value={selectedYear}
       defaultValue={selectedYear}
       onChange={useEffect(() => {setSelectedYear(field.value)})}
    />
   )}
 />
</div>

and ...

<Controller
       control={control}
       name="selectedBirthMonth"
       defaultValue={months[0]}
       render={({ field }) => (
         <SelectBirthMonth
           field={field}
           startYear={startYear}
           selectedYear={selectedYear}
           months={months}
           value={selectedMonth}
           defaultValue={selectedMonth}
           reducedMonths={reducedMonths}
           onChange={useEffect(() => setSelectedMonth(field.value))}
        />
       )}
     />

SelectBirthMonth totally ignored the following code though:

  const [selectedMonth, setSelectedMonth] = useState(months[0]);

  const watchYearChange = () => {
    if(Number(selectedYear.name) == startYear){
      setSelectedMonth(reducedMonths[reducedMonths.length - 1]);
    }
  };

  useEffect(() => watchYearChange(), [selectedYear]);

Meaning, no matter, which year is chosen, the month won't react. What do I miss?

Upvotes: 0

Views: 884

Answers (2)

knoefel
knoefel

Reputation: 6949

I would recommend using a small date library like date-fns to handle date related things. It's a great package, here are the docs for it. Also it can handle i18n for you if this should be a requirement in the future.

I used it in the below CodeSandbox and also corrected a few things:

  • when you use RHF you don't need to setup extra state for your component as RHF will manage the form state for you.
  • it's much simpler if you just use one date for the whole birthdate - this way you will always have the current values for year, month and day and don't need to watch values or use extra defined state

Edit frosty-bash-vv1xf

Upvotes: 1

Katharina Schreiber
Katharina Schreiber

Reputation: 1371

The answer is way too easy, to be working, but it does. Several times I've been reading this post here How to change React-Hook-Form defaultValue with useEffect()?, but could't really understand, where and how do I use setValue. As I assumed, the value of my select just wasn't changing, even though I was watching the sibling state change.

So I put the setValue into the useEffect hook and the rest stayed the same:

  const monthSelect = (data) => {
    setSelectedMonth(months[data.id - 1]);
  };
  
  const watchYearChange = () => {
    if(Number(selectedYear.name) == startYear){
      setSelectedMonth(lastAvaliableMonth)
    }
  };

  useEffect(() => setValue('selectedBirthMonth', lastAvaliableMonth), [selectedYear]);

Here are two siblings just in case:

<Controller
  control={control}
  name="selectedBirthYear"
  defaultValue={years[0]}
  render={({ field }) => (
  <SelectBirthYear
     field={field}
     years={years}
     value={selectedYear}
     defaultValue={selectedYear}
     onChange={useEffect(() => {setSelectedYear(field.value)})}
   />
 )}
/>

... and

    <Controller
      control={control}
      name="selectedBirthMonth"
      defaultValue={selectedMonth}
      render={({ field }) => (
      < SelectBirthMonth 
          field={field}
          startYear={startYear}
          selectedYear={selectedYear}
          months={months}
          value={selectedMonth}
          reducedMonths={reducedMonths}
          onChange={useEffect(() => monthSelect(field.value)), [selectedMonth]}/>
      )}
/>

If this solution somehow is not good, please let me know in the comments. I am a total beginner.

Upvotes: 0

Related Questions