-
-
Notifications
You must be signed in to change notification settings - Fork 753
docs: fix custom components docs #2668
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
Conversation
Update CustomDayButton example to reflect documentation changes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your PR!
Actually, a function that takes React props as arguments is still a functional component. I don’t see any conceptual difference between declaring it directly in the components
prop (as in the example online) and declaring it in the constant above.
(one issue could be the performance while creating the component at each render, so the code there doesn't fit for a good example)
I’m not sure either if the current architecture supports class components. This could be an interesting area to investigate.
We definitely need more practical examples and better documentation related to custom components.
- Using custom components with external UI libraries like react-bootstrap or MUI.
- Enhancing the content of the day cell (e.g. with a Tooltip)
- Implementing a Year navigator who clicking on the Month Caption
- ...
We should collect some “use cases” by searching through this repo’s discussions. Or finding open-source DayPicker implementations that use custom components.
While working on some examples, we could easily identify the parts that are less developer-friendly and adjust DayPicker to be simpler to use in those cases.
In conclusion, to me this PR still doesn't address the core problems in the custom component docs. If you have some time, open an issue where we can focus on “Improving custom component docs.”
Thanks!
function DayButton(props: DayButtonProps) { | ||
const { day, modifiers, ...buttonProps } = props; | ||
|
||
const { setSelected } = React.use(SelectedDateContext); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should be no need to use a new context to store the selected state. DayPicker already runs inside a similar context, which is accessible through the DayPicker hook.
const { setSelected } = React.use(SelectedDateContext); | |
const { select } = useDayPicker(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to use it, but we have some caveats to deal with it:
It doesn't accept undefined
as an argument for the Date
.
Also each select
based on the mode
has specific logic related to them that the consumers may not be aware.
E.g. the select
logic for single
day mode
: https://github.com/gpbl/react-day-picker/blob/main/src/selection/useSingle.tsx
Let me know if we should rethink this example.
I tried to keep the same logic in this PR and focus on the docs update, that's why I avoided changing a lot the example.
There is a difference, actually. I've also confused this in the past because both render props and React components return JSX / React elements, but they are not interchangeable in the way they are used, even though in certain use cases changing one with the other doesn't break the application, it still can potentially cause issues at some point, like #2667, that's why I think this PR is still valid. Starting with the similarity mentioned above, both render props and React components return JSX / React elements, their output is the same, but the difference is how they are called. A render props is always called in user land, meaning always called by user code, mainly inside React components, like in the example in the link of the React docs, if we have a A React component should never be called in user land and only by the React engine, this is what allows components to use React hooks and React manage hook state. There are use cases where misusing them doesn't directly throw an error, but can eventually cause issues like #2667, that's why I think updating the docs to reflect the correct usage would be better.
It should work, it's just not possible to use hooks in a class component, but this can be worked around by having wrapper function component that uses the day picker hooks and pass the data to a class component.
That would be great indeed, I can work on that. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be interested to know if we can remove the React Context and use the useDayPicker
hook instead. If it works, then better use it in the examples you are changing here.
If there are potential issues creating a new component as our code sample suggest, then I do agree to not suggest that approach. We should change also this example in the markdown to something like this:
function CustomDayCell(props: DayProps) {
...
}
function CustomMonthGrid(props: DayProps) {
...
}
<DayPicker
components={{
Day: CustomDayCell,
MonthGrid: CustomMonthGrid,
}}
/>
- To me we lack some meaningful example demonstrating how to use a custom component.
- Examples should show use cases that are easy to understand, and suggest potential applications for a custom components,
- The styled components used in Custom Component too many rerender #2667 are a good example too!
I could check out better your change and I see now the reason to use a context there… but something is missing if we require users to use a context in this manner. Do you think the hook is missing a |
My example wasn't the best one to showcase the need for the React context, sorry about that. The example of passing components={{
// ....
Day: CalendarCustomDay,
}} Therefore, there's no direct way of passing the The only way to solve this that I can think of using the current API is to create a React context.
I'll try to use the But it would also be great to have examples passing custom props that are not managed by the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understood your points... and see how it’s challenging to select days from custom components.
I believe this is a good change and an improvement over the current docs. I appreciate your efforts and patience here! thanks!
You're welcome and thanks @gpbl !
I'll follow your suggestion and look for examples to use as references to add to the docs, I'll open a PR later with more examples. Let me know if you have an opinion about adding a |
Yes, date-picker.luca-felix.com is an excellent example of extending DayPicker. We could also implement the years navigation feature, which has been requested a few times. However, our primary focus should be on making DayPicker easier and more intuitive to extend. I'm not sure if the |
@gpbl The code sandbox has an example of a state that is not managed by the To make the first example work we inevitably need to create a React context, the |
Interesting pattern indeed, and I like how you could expose the issue when passing components that way. What turns me off about Maybe there's a way to pass stricter types: i.e. forbid passing a new function as custom components: components={
Day: () => <div/> // ❌ typecheck fails
Day: CustomDay // ✅ typecheck OK
} For non TypeScript users, we could explain this better in the docs. Would this work? Re: enhancing the user experience of custom components, let’s start creating a some examples that can help users understand how custom components work. (not a priority) |
It's not that uncommon for component-based API, but nowadays it's more common the render props API style which already solves the issue of passing down state and props coming from the parent component.
I think there's no way of enforcing this with TS, both cases are functions that return React elements, and due to TS duck typing they are the same type. For now, I can only think of warning the users in the docs about how the API should be used, and having an example using React context to provide custom props to any custom components if needed.
I'll try to help with that later 👍 |
Aside: examples using Tailwind and DayPicker: https://originui.com/calendars-date-pickers |
What's Changed
CustomDayButton
example to reflect documentation changes.The documentation gives examples of customization based on a render props API, but the customization available is through a component-based API.
E.g. the
DayButton
component is rendered as<components.DayButton {...} />
in theDayPicker
. To follow a render props API style, it should be manually called in theDayPicker
component likecomponents.DayButton({...})
.Using the
components
prop by passing inline functions for the custom components can cause issues like those reported in #2667.This happens because on every render a new function reference is created and mounted as a React component, the React's reconciliation algorithm considers that a new component has been rendered, and because of that it unmounts the previous component and mounts the new one.
This can cause performance issues and also bugs with state resetting because the component is unmounted and then mounted again, resetting the state to the initial values.
Type of Change