Библиотека Nivo опирается на декларативную модель описания данных, где визуализация строится поверх массива объектов. Любые пропуски, некорректные значения или аномалии в данных не «исправляются» автоматически, а интерпретируются в зависимости от типа графика и используемых шкал.
Ключевая особенность поведения заключается в том, что разные
компоненты Nivo по-разному реагируют на значения null,
undefined, NaN, отсутствующие ключи и строки
вместо чисел. Это требует явного контроля качества данных на уровне
подготовки.
null и
undefinedВ большинстве компонентов Nivo:
null интерпретируется как отсутствие точки данныхundefined приводит к пропуску значения при
вычисленияхПример:
const data = [
{ x: 0, y: 10 },
{ x: 1, y: null },
{ x: 2, y: 25 }
]
В линейных графиках это приведёт к разрыву линии, если не задана стратегия интерполяции.
NaN и строковые
значенияNaN считается невалидным числом и ведёт к исключению
точки из визуализации.
Строковые значения:
{ x: 1, y: "20" }
могут приводить к неявным преобразованиям или игнорированию, в зависимости от внутреннего парсинга шкал d3.
Рекомендуется нормализовать данные до передачи в компонент:
const clean = data.map(d => ({
...d,
y: typeof d.y === 'string' ? Number(d.y) : d.y
}))
Компонент ResponsiveLine особенно чувствителен к
пропускам.
По умолчанию:
null разрывает линию<ResponsiveLine
data={data}
enableArea={false}
/>
Для управления поведением используется стратегия
defined:
const data = [
{
id: "series1",
data: [
{ x: 0, y: 10 },
{ x: 1, y: null },
{ x: 2, y: 20 }
]
}
]
Можно предварительно фильтровать:
const filtered = data.map(series => ({
...series,
data: series.data.filter(d => d.y != null)
}))
В ResponsiveBar отсутствие ключа или null
приводит к:
{ country: "A", value: null }
Такие случаи требуют явного заполнения:
const normalized = data.map(d => ({
...d,
value: d.value ?? 0
}))
или фильтрации категорий:
const filtered = data.filter(d => d.value != null)
В ResponsivePie пропуски обрабатываются строже:
NaN и null полностью исключаютсяconst data = [
{ id: "A", value: 10 },
{ id: "B", value: null }
]
Результат: сегмент B не отображается.
При динамических данных рекомендуется:
const safe = data
.filter(d => typeof d.value === "number" && !isNaN(d.value))
В ResponsiveScatterPlot пропуски влияют на распределение
точек:
NaN исключаютсяx или y приводит к
пропуску объекта целиком{ x: null, y: 10 } // не отображается
При частично заполненных координатах важно нормализовать структуру:
const safePoints = data.map(p => ({
x: p.x ?? 0,
y: p.y ?? 0
}))
или полностью исключать некорректные:
const safePoints = data.filter(p =>
typeof p.x === "number" && typeof p.y === "number"
)
В ResponsiveHeatMap пропуски особенно критичны, так как
каждая ячейка должна иметь числовое значение.
null может интерпретироваться как отсутствие цветаNaN приводит к пропуску рендера ячейки{
id: "row1",
data: [
{ x: "A", y: null },
{ x: "B", y: 20 }
]
}
Типичный подход:
const normalized = data.map(row => ({
...row,
data: row.data.map(cell => ({
...cell,
y: typeof cell.y === "number" ? cell.y : 0
}))
}))
Базовый подход:
const sanitize = d => typeof d === "number" && !isNaN(d)
Использование:
const clean = data.filter(d => sanitize(d.value))
Используется, когда потеря данных недопустима:
const filled = data.map((d, i, arr) => ({
...d,
value: d.value ?? (arr[i - 1]?.value ?? 0)
}))
Для временных рядов:
const interpolate = (a, b) => (a + b) / 2
Nivo позволяет использовать функции доступа к данным:
value: d => d.metrics?.value
При отсутствии защиты это приводит к:
undefinedvalue: d => Number(d.metrics?.value ?? 0)
Tooltip-компоненты часто вызываются даже для частично некорректных данных.
undefinedtooltip: ({ point }) => {
const value = Number.isFinite(point.data.y)
? point.data.y
: "нет данных"
return (
<div>
Значение: {value}
</div>
)
}
Шкалы d3, используемые внутри Nivo:
NaNundefineddomain={[0, "20"]} // некорректно
const domain = [
Math.min(...data.map(d => d.y || 0)),
Math.max(...data.map(d => d.y || 0))
]
Причины:
nullПричины:
Причины:
null в seriesНадёжная схема подготовки:
const prepare = data =>
data
.filter(d => d != null)
.map(d => ({
...d,
value: Number.isFinite(d.value) ? d.value : 0
}))
Ключевой принцип — график не должен получать «сырые» данные. Любая визуализация должна опираться на предсказуемую структуру, где: