Kontexte
Das Headless-Paket stellt Context-Provider zur Verfügung, die die Hooks umschließen und ihren Zustand über React Context verfügbar machen. Dies ermöglicht das Compound-Component-Pattern – teilen Sie Ihre Benutzeroberfläche in kleine, fokussierte Unterkomponenten auf, die den Zustand aus dem Kontext lesen, anstatt Props weiterzugeben.
Warum Kontexte?
Ohne Kontext müssten Sie jeden Teil des Zustands über Props durchreichen:
// Ohne Kontext – Prop Drillingfunction MyDatePicker() { const picker = useDatePicker({ value, onChange }); return ( <MyTrigger displayValue={picker.displayValue} onToggle={picker.handleToggle} /> <MyCalendar calendar={picker.calendar} getDayProps={picker.getDayProps} ... /> <MyFooter onConfirm={picker.handleConfirm} onCancel={picker.handleCancel} ... /> );}Mit Kontext konsumieren Unterkomponenten direkt, was sie benötigen:
// Mit Kontext – Compound Componentsfunction MyDatePicker() { return ( <DatePickerProvider value={value} onChange={onChange}> <ExampleWrapper variant="headless" client:visible> <MyTrigger client:visible /> </ExampleWrapper> <ExampleWrapper variant="headless" client:visible> <MyCalendar client:visible /> </ExampleWrapper> <ExampleWrapper variant="headless" client:visible> <MyFooter client:visible /> </ExampleWrapper> </DatePickerProvider> );}
function MyTrigger() { const { displayValue, handleToggle } = useDatePickerContext(); return <button onClick={handleToggle}>{displayValue}</button>;}Verfügbare Provider
| Provider | Intern verwendeter Hook | Consumer-Hook |
|---|---|---|
DatePickerProvider | useDatePicker | useDatePickerContext() |
DateRangePickerProvider | useDateRangePicker | useDateRangePickerContext() |
DateTimePickerProvider | useDateTimePicker | useDateTimePickerContext() |
DateRangeTimePickerProvider | useDateRangeTimePicker | useDateRangeTimePickerContext() |
Jeder Provider rendert sowohl einen typisierten Kontext (spezifisch für diesen Picker-Typ) als auch den vereinheitlichten PickerContext (für alle Typen gemeinsam).
import { DatePickerProvider, DateRangePickerProvider, DateTimePickerProvider, DateRangeTimePickerProvider,} from "react-date-range-picker-headless";DatePickerProvider
Umschließt useDatePicker. Stellt DatePickerContext + PickerContext zur Verfügung.
import type { DatePickerProviderProps } from "react-date-range-picker-headless";| Prop | Typ | Beschreibung |
|---|---|---|
children | ReactNode | Erforderlich. Kindkomponenten. |
value | Date | null | Erforderlich. Aktuelles Datum. |
onChange | (date: Date | null) => void | Erforderlich. Change-Handler. |
inline | boolean | Inline rendern (kein Popup). |
size | DatePickerSize | Größenvariante. |
placeholder | string | Platzhalter für den Trigger. |
name | string | Name des Formularfelds. |
| …rest | BaseDatePickerOptions | Alle Basisoptionen (minDate, maxDate, locale, etc.). |
Consumer-Hook: useDatePickerContext() gibt UseDatePickerReturn zurück.
DateRangePickerProvider
Umschließt useDateRangePicker. Stellt DateRangePickerContext + PickerContext zur Verfügung.
import type { DateRangePickerProviderProps } from "react-date-range-picker-headless";| Prop | Typ | Beschreibung |
|---|---|---|
children | ReactNode | Erforderlich. Kindkomponenten. |
value | { start: Date | null; end: Date | null } | Erforderlich. Aktueller Bereich. |
onChange | (value: { start: Date | null; end: Date | null }) => void | Erforderlich. Change-Handler. |
inline | boolean | Inline rendern. |
size | DatePickerSize | Größenvariante. |
placeholder | string | Platzhalter für den Trigger. |
name | string | Name des Formularfelds. |
maxDays | number | Maximale Tage im Bereich. |
minDays | number | Minimale Tage im Bereich. |
presets | DateRangePreset[] | Bereichsvoreinstellungen. |
allowSingleDateInRange | boolean | Einzelnen Tag im Bereich erlauben. |
| …rest | BaseDatePickerOptions | Alle Basisoptionen. |
Consumer-Hook: useDateRangePickerContext() gibt UseDateRangePickerReturn zurück.
DateTimePickerProvider
Umschließt useDateTimePicker. Stellt DateTimePickerContext + PickerContext zur Verfügung.
import type { DateTimePickerProviderProps } from "react-date-range-picker-headless";| Prop | Typ | Beschreibung |
|---|---|---|
children | ReactNode | Erforderlich. Kindkomponenten. |
value | Date | null | Erforderlich. Aktuelles Datum und Uhrzeit. |
onChange | (dateTime: Date | null) => void | Erforderlich. Change-Handler. |
time | TimeConfig | Zeitkonfiguration. |
inline | boolean | Inline rendern. |
size | DatePickerSize | Größenvariante. |
placeholder | string | Platzhalter für den Trigger. |
name | string | Name des Formularfelds. |
| …rest | BaseDatePickerOptions | Alle Basisoptionen. |
Consumer-Hook: useDateTimePickerContext() gibt UseDateTimePickerReturn zurück.
DateRangeTimePickerProvider
Umschließt useDateRangeTimePicker. Stellt DateRangeTimePickerContext + PickerContext zur Verfügung.
import type { DateRangeTimePickerProviderProps } from "react-date-range-picker-headless";| Prop | Typ | Beschreibung |
|---|---|---|
children | ReactNode | Erforderlich. Kindkomponenten. |
value | { start: Date | null; end: Date | null } | Erforderlich. Aktueller Bereich. |
onChange | (value: { start: Date | null; end: Date | null }) => void | Erforderlich. Change-Handler. |
time | TimeConfig | Zeitkonfiguration. |
inline | boolean | Inline rendern. |
size | DatePickerSize | Größenvariante. |
placeholder | string | Platzhalter für den Trigger. |
name | string | Name des Formularfelds. |
maxDays | number | Maximale Tage im Bereich. |
minDays | number | Minimale Tage im Bereich. |
presets | DateRangePreset[] | Bereichsvoreinstellungen. |
allowSingleDateInRange | boolean | Einzelnen Tag im Bereich erlauben. |
| …rest | BaseDatePickerOptions | Alle Basisoptionen. |
Consumer-Hook: useDateRangeTimePickerContext() gibt UseDateRangeTimePickerReturn zurück.
PickerContext (Vereinheitlicht)
Jeder Provider injiziert auch einen vereinheitlichten PickerContext. Dies ist ein Superset-Kontext, der alle Felder von jedem Picker-Typ enthält, mit optionalen Feldern für picker-spezifische Werte.
import { usePickerContext } from "react-date-range-picker-headless";
function MyGenericHeader() { const { locale, handlePrevMonth, handleNextMonth, calendars } = usePickerContext(); return ( <div> <button onClick={handlePrevMonth}>{locale.prevMonth}</button> <span>{locale.formatMonthYear(calendars[0].month)}</span> <button onClick={handleNextMonth}>{locale.nextMonth}</button> </div> );}Der vereinheitlichte Kontext ermöglicht es Ihnen, gemeinsame Unterkomponenten zu erstellen, die für alle Picker-Typen funktionieren. Die Styled-Pakete (styled, tailwind3, tailwind4) verwenden dieses Muster intern – ihre Header, Grid, Footer usw. Komponenten konsumieren alle den PickerContext.
PickerContext-Felder
import { PickerContext, usePickerContext } from "react-date-range-picker-headless";import type { PickerContextValue } from "react-date-range-picker-headless";function usePickerContext(): PickerContextValue;Wirft einen Fehler, wenn es außerhalb eines Picker-Providers verwendet wird.
Kernfelder (immer vorhanden)
| Feld | Typ |
|---|---|
isOpen | boolean |
locale | Locale |
focusedDate | Date | null |
displayValue | string |
hasValue | boolean |
canConfirm | boolean |
calendars | CalendarMonth[] |
getDayProps | (date: Date, referenceMonth?: Date) => DayProps |
handleToggle | () => void |
handleOpen | () => void |
handleClose | () => void |
handleConfirm | () => void |
handleCancel | () => void |
handleClear | () => void |
handleGoToToday | () => void |
handlePrevMonth | () => void |
handleNextMonth | () => void |
handleKeyDown | (e: KeyboardEvent<HTMLElement>) => void |
handleDateClick | (date: Date) => void |
containerRef | RefObject<HTMLDivElement | null> |
popupRef | RefObject<HTMLDivElement | null> |
years | number[] |
months | number[] |
handleYearSelect | (year: number, calendarIndex?: number) => void |
handleMonthSelect | (month: number, calendarIndex?: number) => void |
UI-Durchgangsfelder
| Feld | Typ |
|---|---|
value | unknown |
endName | string | undefined |
required | boolean | undefined |
inline | boolean | undefined |
showOutsideDays | boolean | undefined |
size | DatePickerSize | undefined |
placeholder | string | undefined |
name | string | undefined |
captionLayout | CaptionLayout | undefined |
Bereichsspezifische Felder (optional)
| Feld | Typ |
|---|---|
handleDateHover | ((date: Date | null) => void) | undefined |
presets | DateRangePreset[] | undefined |
handlePresetClick | ((preset: DateRangePreset) => void) | undefined |
activePresetIndex | number | undefined |
Einzelne Zeitfelder (optional, DateTimePicker)
| Feld | Typ |
|---|---|
resolvedTimeConfig | Required<TimeConfig> | undefined |
tempHour | number | undefined |
tempMinute | number | undefined |
tempSecond | number | undefined |
tempPeriod | TimePeriod | undefined |
handleHourChange | ((hour: number) => void) | undefined |
handleMinuteChange | ((minute: number) => void) | undefined |
handleSecondChange | ((second: number) => void) | undefined |
handlePeriodChange | ((period: TimePeriod) => void) | undefined |
isTimePickerOpen | boolean | undefined |
handleTimePickerOpen | (() => void) | undefined |
handleTimePickerClose | (() => void) | undefined |
handleTimePickerConfirm | (() => void) | undefined |
handleTimePickerCancel | (() => void) | undefined |
timePickerRef | RefObject<HTMLDivElement | null> | undefined |
timeDisplayValue | string | undefined |
Bereichs-Zeitfelder (optional, DateRangeTimePicker)
| Feld | Typ |
|---|---|
startHour | number | undefined |
startMinute | number | undefined |
startSecond | number | undefined |
startPeriod | TimePeriod | undefined |
endHour | number | undefined |
endMinute | number | undefined |
endSecond | number | undefined |
endPeriod | TimePeriod | undefined |
handleStartHourChange | ((hour: number) => void) | undefined |
handleStartMinuteChange | ((minute: number) => void) | undefined |
handleStartSecondChange | ((second: number) => void) | undefined |
handleStartPeriodChange | ((period: TimePeriod) => void) | undefined |
handleEndHourChange | ((hour: number) => void) | undefined |
handleEndMinuteChange | ((minute: number) => void) | undefined |
handleEndSecondChange | ((second: number) => void) | undefined |
handleEndPeriodChange | ((period: TimePeriod) => void) | undefined |
isStartTimePickerOpen | boolean | undefined |
isEndTimePickerOpen | boolean | undefined |
handleStartTimePickerOpen | (() => void) | undefined |
handleStartTimePickerClose | (() => void) | undefined |
handleStartTimePickerConfirm | (() => void) | undefined |
handleStartTimePickerCancel | (() => void) | undefined |
handleEndTimePickerOpen | (() => void) | undefined |
handleEndTimePickerClose | (() => void) | undefined |
handleEndTimePickerConfirm | (() => void) | undefined |
handleEndTimePickerCancel | (() => void) | undefined |
startTimePickerRef | RefObject<HTMLDivElement | null> | undefined |
endTimePickerRef | RefObject<HTMLDivElement | null> | undefined |
startTimeDisplayValue | string | undefined |
endTimeDisplayValue | string | undefined |
Typisierte vs. Vereinheitlichte Kontexte
| Anwendungsfall | Welcher Kontext |
|---|---|
| Erstellen einer gemeinsamen Komponente, die für alle Picker-Typen funktioniert | usePickerContext() |
| Erstellen einer Komponente, die spezifisch für einen Picker ist, mit voller Typsicherheit | useDatePickerContext(), useDateRangePickerContext(), etc. |
Die typisierten Kontexte geben den exakten Rückgabetyp des Hooks zurück, sodass Sie eine vollständige TypeScript-Inferenz erhalten. Der vereinheitlichte Kontext verwendet optionale Felder für picker-spezifische Werte.
Dual-Kontext-Muster
Jeder Provider rendert zwei verschachtelte Kontexte:
<SpecificPickerContext.Provider value={hookReturn}> <PickerContext.Provider value={unifiedValue}>{children}</PickerContext.Provider></SpecificPickerContext.Provider>- Verwenden Sie
usePickerContext()für gemeinsame Unterkomponenten (funktioniert mit jedem Picker) - Verwenden Sie den typisierten Consumer-Hook für picker-spezifische Logik (volle Typsicherheit)
Beispiel: Zusammengesetzter Date Picker
import { useState } from "react";import { DatePickerProvider, usePickerContext, type DayProps,} from "react-date-range-picker-headless";
/** A child component that reads from PickerContext instead of receiving props. */function CalendarDisplay() { const ctx = usePickerContext();
const calendar = ctx.calendars[0];
return ( <div style={{ border: "1px solid #ccc", padding: 16, borderRadius: 8, background: "#fff" }}> {/* Header */} <div style={{ display: "flex", justifyContent: "space-between", marginBottom: 8 }}> <button onClick={ctx.handlePrevMonth}>←</button> <span>{ctx.locale.formatMonthYear(calendar.month)}</span> <button onClick={ctx.handleNextMonth}>→</button> </div>
{/* Weekdays */} <div style={{ display: "grid", gridTemplateColumns: "repeat(7, 1fr)", textAlign: "center" }}> {ctx.locale.weekdays.map((wd) => ( <div key={wd} style={{ fontSize: 12, color: "#888", padding: 4 }}> {wd} </div> ))} </div>
{/* Days */} <div style={{ display: "grid", gridTemplateColumns: "repeat(7, 1fr)", textAlign: "center" }}> {calendar.days.map((date, i) => { if (!date) return <div key={i} />; const dp: DayProps = ctx.getDayProps(date); return ( <button key={i} onClick={() => ctx.handleDateClick(date)} disabled={dp.isDisabled} style={{ padding: 8, cursor: dp.isDisabled ? "not-allowed" : "pointer", background: dp.isSelected ? "#3b82f6" : "transparent", color: dp.isSelected ? "#fff" : dp.isToday ? "#3b82f6" : dp.isDisabled ? "#ccc" : "#333", fontWeight: dp.isToday ? "bold" : "normal", border: "none", borderRadius: 4, }} > {dp.day} </button> ); })} </div>
{/* Footer */} <div style={{ display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 8 }}> <button onClick={ctx.handleCancel}>Cancel</button> <button onClick={ctx.handleConfirm} disabled={!ctx.canConfirm}> Confirm </button> </div> </div> );}
/** Main example wrapping with DatePickerProvider so children can use usePickerContext. */function ContextExample() { const [value, setValue] = useState<Date | null>(null);
return ( <div style={{ fontFamily: "system-ui" }}> <DatePickerProvider value={value} onChange={setValue} initialOpen> <div style={{ marginBottom: 8, fontSize: 14, color: "#555" }}> Context-driven calendar (always open via initialOpen): </div> <CalendarDisplay /> </DatePickerProvider> </div> );}