Skip to content
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

Example of how to make a controlled component? #4

Closed
ArjanJ opened this issue Jan 17, 2023 · 6 comments
Closed

Example of how to make a controlled component? #4

ArjanJ opened this issue Jan 17, 2023 · 6 comments

Comments

@ArjanJ
Copy link

ArjanJ commented Jan 17, 2023

Not an issue, but some guidance would be appreciated on how to set this up so that it works like a controlled component, where the "value" (selected dates) is passed from a parent component or state, and is updated through an onChange handler of sorts. An example of how to use this with react-hook-form would be amazing.

The only thing I've found so far to react to changes to selectedDates is by setting up a useEffect like this, which honestly doesn't work very well:

const MyDatepickerComponent = ({ onChange }) => {
  const { data } = useDatePicker()
 
  useEffect(() => {
    onChange(data.selectedDates);
  }, [data.selectedDates])

  return (
    <div>...</div>
   )
}

What would be cool is something like this, where the onChange callback is called internally, and selectedDates is the state value from the parent component that determines what days are selected. (Making this flow of data one way/controlled):

const MyDatepickerComponent = ({ onChange, selectedDates }) => {
  const { data } = useDatePicker({ dates: { onChange, selectedDates }})

  return (
    <div>...</div>
   )
}

Maybe there is a better way to do this, idk, any tips would be appreciated.

@Feshchenko
Copy link
Contributor

Feshchenko commented Jan 18, 2023

Hi, Arjan.
I would think about this approach.
As date-picker has multiple modes single, multiple, and range it is hard to determine what is onChange means in all cases.

The first workaround is correct, you can wrap change of selectedDates or formatedDates into useEffect
The second workaround is when you are using propGetters you can pass onClick to the dayButton.
You can see an example here: https://github.com/rehookify/datepicker#with-hook

const { data: { selectedDates }  } = useDatePicker()

const onDayClick = (evt: MouseEvent<HTMLElement>, date: Date) => {
  evt.stopPropagation();
  onChange(selectedDates);
}

...

<ul>
  {days.map((dpDay) => (
    <li key={`${month}-${dpDay.date}`}>
      <button {...dayButton(dpDay, { onDayClick })}>{dpDay.day}</button>
    </li>
  ))}
</ul>

About selectedDates: you could pass selectedDates to the config:

const { data } = useDatePicker({ dates: { selectedDates: [new Date(), new Date(2023, 0, 18)] }});

@ArjanJ
Copy link
Author

ArjanJ commented Jan 18, 2023

Right, I would image that onChange would be passed selectedDates, that way it would work for single, multiple, or range modes. I did try passing a callback to dayButton but it's not that useful if you're using multiple or range modes, because then you have to create your own state management solution for tracking more than one selected date (which is what this library is already doing). I think getting the value of selectedDates in a more event driven way would be ideal, since it already has the value we're looking for, it's just not accessible in the right place and right time.

About selectedDates: you could pass selectedDates to the config:

This works fine for setting the initial state, but if you update the value of selectedDates after the initial rendering it doesn't update. For example, clicking the "Clear" button here doesn't do anything to the state of useDatePicker, the initial dates are still selected.

const Example = () => {
  const [myDates, setMyDates] = useState([new Date(), addDays(new Date(), 3)]);

  const dp = useDatePicker({
    dates: {
      mode: "range",
      selectedDates: myDates, // Does not update when `myDates` changes
    },
  });

  return (
    <>
      <DatePickerUI data={dp.data} propGetters={dp.propGetters} />
      <button onClick={() => setMyDates([])}>Clear dates</button>
    </>
  );
};

@Feshchenko
Copy link
Contributor

Feshchenko commented Jan 18, 2023

If you need this feature urgently you can use action setDay https://github.com/rehookify/datepicker#setday
Personally, I don't like a controlled approach, but this is not a huge change.
I will add this to the backlog and will do it after the time picker functionality.

@ArjanJ
Copy link
Author

ArjanJ commented Jan 18, 2023

No worries, thanks for getting back to me. Not needed urgently but it would be nice to have, maybe I'll submit a PR if I get some extra time.

@Feshchenko
Copy link
Contributor

Hey @ArjanJ.

I've been thinking about date-pickers and after some research, I realized that a controlled date-picker is a common pattern. So, I went ahead and made a new release, version 3.0.0, which is fully controlled and available now on our GitHub repository.
I hope you'll find it useful and enjoy using it! Let me know if you have any feedback or questions.

@ArjanJ
Copy link
Author

ArjanJ commented Jan 21, 2023

Thank you for this update, it works really well!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants