Библиотека Nivo опирается на строго определённые структуры данных, которые отличаются в зависимости от типа графика. Предобработка данных становится ключевым этапом, поскольку API-ответы, базы данных и аналитические системы почти никогда не возвращают данные в форме, готовой для визуализации.
Для разных компонентов используются разные схемы:
{ x, y }id и valueНесоответствие этим структурам приводит к некорректному отображению или полной потере данных на графике, поэтому первичная задача — приведение данных к ожидаемому контракту.
Реальные данные часто приходят в виде вложенных объектов или нестабильных структур:
{
"result": [
{ "name": "A", "metrics": { "sales": 120, "profit": 30 } },
{ "name": "B", "metrics": { "sales": 90, "profit": 10 } }
]
}
Для Nivo Bar Chart требуется преобразование в:
[
{ "name": "A", "sales": 120, "profit": 30 },
{ "name": "B", "sales": 90, "profit": 10 }
]
Ключевой этап — распаковка вложенных объектов и выравнивание структуры до плоского уровня, где каждое измерение становится отдельным полем.
Типовые операции:
metrics.sales)id, timestamp,
если не используются)Часто данные поступают на уровне событий, а не агрегированных значений:
[
{ "category": "A", "value": 10 },
{ "category": "A", "value": 15 },
{ "category": "B", "value": 5 }
]
Для визуализации требуется агрегация:
[
{ "category": "A", "value": 25 },
{ "category": "B", "value": 5 }
]
Реализация сводится к использованию reduce:
const grouped = Object.values(
data.reduce((acc, item) => {
if (!acc[item.category]) {
acc[item.category] = { category: item.category, value: 0 }
}
acc[item.category].value += item.value
return acc
}, {})
)
В сложных случаях применяется многомерная агрегация по нескольким ключам (например, категория + дата).
Bar chart в Nivo требует табличной структуры:
[
{ country: "Kazakhstan", 2023: 120, 2024: 140 },
{ country: "Russia", 2023: 100, 2024: 130 }
]
Ключевой момент — разделение:
indexBy)keys)Типичная трансформация из “длинного” формата:
const pivot = (data) => {
const result = {}
data.forEach(({ country, year, value }) => {
if (!result[country]) result[country] = { country }
result[country][year] = value
})
return Object.values(result)
}
Line chart требует структуры серий:
[
{
id: "sales",
data: [
{ x: "2024-01-01", y: 120 },
{ x: "2024-01-02", y: 90 }
]
}
]
Предобработка временных рядов включает:
const parsed = data.map(d => ({
x: new Date(d.date),
y: Number(d.value)
}))
const seriesMap = {}
data.forEach(item => {
if (!seriesMap[item.metric]) {
seriesMap[item.metric] = { id: item.metric, data: [] }
}
seriesMap[item.metric].data.push({
x: item.date,
y: item.value
})
})
series.forEach(s => {
s.data.sort((a, b) => new Date(a.x) - new Date(b.x))
})
Без сортировки линии могут отображаться с артефактами и “ломаной” геометрией.
Pie chart требует максимально упрощённую структуру:
[
{ id: "Chrome", value: 60 },
{ id: "Firefox", value: 25 },
{ id: "Safari", value: 15 }
]
Предобработка обычно включает:
const pieData = Object.entries(raw).map(([key, value]) => ({
id: key,
value: Math.round(value)
}))
Особое внимание уделяется удалению отрицательных значений, так как круговые диаграммы их не поддерживают.
Stacked bar/area charts требуют согласованного набора ключей:
[
{ month: "Jan", desktop: 120, mobile: 80 },
{ month: "Feb", desktop: 100, mobile: 90 }
]
Предобработка включает:
const normalizeKeys = (data, keys) =>
data.map(item => {
const normalized = { ...item }
keys.forEach(k => {
if (normalized[k] == null) normalized[k] = 0
})
return normalized
})
Перед передачей в визуализацию необходимо устранить некорректные значения:
data.filter(d => d.value != null && !isNaN(d.value))
const safe = data.map(d => ({
...d,
value: d.value ?? 0
}))
const clamp = (v, min, max) => Math.min(max, Math.max(min, v))
Nivo чувствителен к порядку элементов в некоторых типах графиков (особенно line и area charts).
Рекомендуемые принципы:
const stableSort = (data) =>
[...data].sort((a, b) => a.label.localeCompare(b.label))
При использовании React повторная предобработка данных на каждом рендере приводит к деградации производительности.
Используется useMemo:
const chartData = useMemo(() => {
return transform(rawData)
}, [rawData])
Важно учитывать:
При подготовке данных для временных графиков критична корректная работа с датами:
"x": "2024-01-01"
Может интерпретироваться по-разному в разных окружениях.
const normalized = data.map(d => ({
x: new Date(d.x).getTime(),
y: d.y
}))
Частая проблема — числовые значения в виде строк:
{ "value": "42" }
Приведение:
const cast = (d) => ({
...d,
value: Number(d.value)
})
Дополнительно:
NaN заменяется на 0 или фильтруетсяГрафик ожидает value, а передаётся val или
amount.
Особенно критично для линий и областей.
Числа + строки в одном поле приводят к некорректным масштабам.
Разные элементы массива имеют разные наборы полей, что ломает оси и легенды.
Nivo не “разворачивает” объекты автоматически — структура должна быть плоской или строго типизированной.