В библиотеке визуализации Nivo обработчики событий тесно связаны с типами данных графиков и внутренними структурами библиотечных компонентов. Основной подход заключается в том, что каждый интерактивный график предоставляет строго типизированные коллбеки, отражающие контекст взаимодействия: точку данных, серию, индекс, координаты курсора и метаданные визуального элемента.
Типизация событий строится вокруг React-событий и обобщённых структур
Nivo, где ключевую роль играют Datum, Serie,
BarDatum, Point, ArcDatum. В
TypeScript-окружении это позволяет не только повысить предсказуемость
логики, но и исключить ошибки при доступе к полям данных.
Большинство компонентов Nivo используют унифицированный стиль описания событий:
onClick: (datum: Datum, event: React.MouseEvent) => void
onMouseEnter: (datum: Datum, event: React.MouseEvent) => void
onMouseLeave: (datum: Datum, event: React.MouseEvent) => void
Datum в данном контексте представляет собой расширенный
объект данных, обогащённый служебными полями (индексы, цвета,
идентификаторы серии). Тип конкретного Datum зависит от
графика.
Например, для столбчатой диаграммы:
import { BarDatum } from '@nivo/bar'
Для линейного графика:
import { Point } from '@nivo/line'
Для круговых диаграмм:
import { ArcDatum } from '@nivo/pie'
Компонент ResponsiveBar предоставляет расширенный набор
событий, связанных с отдельными столбцами.
import { ResponsiveBar, BarDatum } from '@nivo/bar'
type BarClickHandler = (bar: BarDatum, event: React.MouseEvent) => void
Объект BarDatum включает как исходные значения, так и
вычисленные свойства:
id — идентификатор категорииvalue — числовое значениеindexValue — ось категорийcolor — вычисленный цветdata — оригинальный объект строки данныхПример строгой типизации:
const handleBarClick: BarClickHandler = (bar, event) => {
console.log(bar.id)
console.log(bar.value)
console.log(bar.data)
}
Важный аспект заключается в том, что bar.data не теряет
исходную структуру, что позволяет безопасно извлекать дополнительные
поля без кастов.
Линейные графики (ResponsiveLine) используют структуру
точек:
import { Point } from '@nivo/line'
Сигнатуры событий:
type PointClickHandler = (point: Point, event: React.MouseEvent) => void
Тип Point содержит:
x и y — координаты данныхserieId — идентификатор серииdata — исходный объект данныхindex — индекс точки в серииcolor — цвет точкиПример обработки:
const handlePointClick = (point: Point, event: React.MouseEvent) => {
const series = point.serieId
const value = point.data.y
}
Особенность линейных графиков заключается в наличии производных событий, связанных с сегментами линий:
onMouseEnter: (point: Point, event: React.MouseEvent) => void
onMouseMove: (point: Point, event: React.MouseEvent) => void
В ResponsivePie обработчики работают с дугами:
import { ArcDatum } from '@nivo/pie'
Типовая сигнатура:
type ArcClickHandler = (arc: ArcDatum, event: React.MouseEvent) => void
Структура ArcDatum включает:
id — идентификатор секцииvalue — значениеformattedValue — форматированное отображениеcolor — вычисленный цвет сектораlabel — текстовая меткаПример:
const handleArcClick = (arc: ArcDatum) => {
const percentage = arc.value
const label = arc.label
}
Важной особенностью является наличие предрассчитанных углов:
arc.arc.startAnglearc.arc.endAngleЭти данные позволяют реализовывать сложные визуальные эффекты, зависящие от геометрии сектора.
Nivo активно использует дженерики для сохранения исходной структуры данных. В большинстве компонентов можно передать собственный тип данных:
type MyData = {
category: string
amount: number
}
Использование в Bar:
<ResponsiveBar<MyData>
data={data}
keys={['amount']}
indexBy="category"
onCl ick={(bar, event) => {
bar.data.category
bar.data.amount
}}
/>
Здесь bar.data становится строго типизированным объектом
MyData.
События наведения (hover) в Nivo часто используются для
построения интерактивных подсказок и синхронизации графиков.
onMouseEnter: (datum, event) => void
onMouseMove: (datum, event) => void
onMouseLeave: (datum, event) => void
Тип event остаётся стандартным React:
React.MouseEvent<SVGElement, MouseEvent>
Особенность заключается в том, что Nivo передаёт уже вычисленный datum, а не сырые координаты DOM-элемента.
Некоторые компоненты предоставляют доступ к координатам курсора:
onMouseMove: (datum, event) => {
const x = event.clientX
const y = event.clientY
}
Однако важно учитывать, что координаты относятся к viewport, а не к SVG-системе координат. Для преобразования используются внутренние утилиты Nivo, которые не всегда экспонируются напрямую.
Легенды также поддерживают события:
onClick: (item, event) => void
Тип элемента легенды:
type LegendItem = {
id: string
label: string
color: string
data: unknown
}
Часто data содержит ссылку на исходный
datum, что позволяет синхронизировать легенду с
графиком.
При создании сложных графиков возникает необходимость расширения типов событий:
type ExtendedBarDatum<T> = BarDatum & {
data: T
}
Использование:
const onCl ick = (bar: ExtendedBarDatum<MyData>, event: React.MouseEvent) => {
bar.data.category
}
Такой подход позволяет сохранить типовую безопасность при добавлении доменной логики.
В различных графиках Nivo одинаковые по смыслу события могут иметь
разные типы datum. Например:
BarDatum содержит indexValuePoint содержит serieIdArcDatum содержит arcЭто приводит к необходимости явного разделения обработчиков:
type ChartEvent =
| { type: 'bar'; dat a: BarDatum }
| { type: 'line'; dat a: Point }
| { type: 'pie'; dat a: ArcDatum }
Дальнейшая обработка строится через дискриминируемые объединения:
const handleEvent = (e: ChartEvent) => {
if (e.type === 'bar') {
e.data.value
}
}
Все события Nivo основаны на React.SyntheticEvent, что
накладывает ограничения:
event.target может быть нестабильнымТипизация:
React.MouseEvent<SVGElement, MouseEvent>
При необходимости сохранения события используется:
event.persist()
В некоторых случаях графики обогащаются пользовательскими полями:
type ExtendedDatum = BarDatum & {
metadata: {
region: string
category: string
}
}
Использование:
onCl ick={(bar: ExtendedDatum) => {
bar.metadata.region
}}
Это особенно важно при построении аналитических панелей, где визуальный слой отделён от доменной модели.
Основное преимущество типизации обработчиков в Nivo заключается в снижении зависимости от runtime-структур. Ошибки доступа к несуществующим полям заменяются компиляционными ошибками.
Типичные классы ошибок, устраняемых TypeScript:
datum.value в неподходящем
графикеid и indexValuelegend item и
chart datumПри масштабировании визуальных систем обработчики событий Nivo часто выносятся в слой абстракции:
type ChartHandlers<T> = {
onClick?: (datum: T, event: React.MouseEvent) => void
onHover?: (datum: T, event: React.MouseEvent) => void
}
Далее конкретные графики подставляют соответствующие типы:
const barHandlers: ChartHandlers<BarDatum> = { ... }
const lineHandlers: ChartHandlers<Point> = { ... }
Такой подход снижает связность между UI-компонентами и логикой обработки событий.
В сложных интерфейсах события часто интегрируются с состоянием:
const handleClick = (bar: BarDatum) => {
setSelectedId(bar.id)
}
Типизация гарантирует согласованность состояния с визуальной моделью, исключая несоответствия между выбранными элементами и реальными данными графика.