I am using the VictoryNative chart library in my React Native project to render a bar chart based on dynamically calculated data passed via the insights prop. However, when I try to render the chart, I encounter the following error:
typescript Copy Edit Error: Exception in HostFunction: Value is undefined, expected a number What I’m trying to do: I am dynamically calculating the earnings for different platforms (youtube, spotify, etc.) based on data passed in insights. I need to plot the earnings on a bar chart for the top 10 platforms.
import {StyleSheet, Text, useWindowDimensions, View} from 'react-native';
import React, {useEffect, useState} from 'react';
import {
Area,
Bar,
CartesianChart,
Line,
useChartPressState,
} from 'victory-native';
import {
Circle,
Image,
LinearGradient,
Text as SKText,
useFont,
useImage,
vec,
} from '@shopify/react-native-skia';
import {abbreviateNumber} from '../../../../common/Common';
interface PlatformTotals {
platform: string;
earnings: number;
}
const PlatformBarChart = ({ insights }: { insights: any }) => {
const font = useFont(require('../../../../../assets/fonts/DMSans-Regular.ttf'), 10);
const { width, height } = useWindowDimensions();
const [platformData, setPlatformsData] = useState<PlatformTotals[]>([]);
useEffect(() => {
if (insights) calculatePlatformTotals(insights);
}, [insights]);
const calculatePlatformTotals = (data: any) => {
const platformTotals: { [platform: string]: number } = {};
// Iterate over each platform in the data
Object.keys(data).forEach(platform => {
if (platform === 'Total Revenue') return;
const platformData = data[platform];
Object.keys(platformData).forEach(year => {
const yearData = platformData[year];
Object.keys(yearData).forEach(month => {
const monthData = yearData[month];
// Accumulate earnings if available
if (monthData && monthData.earnings) {
if (platformTotals[platform]) {
platformTotals[platform] += monthData.earnings;
} else {
platformTotals[platform] = monthData.earnings;
}
}
});
});
});
// Convert and sort the platform totals
const sortedPlatforms = Object.entries(platformTotals)
.map(([platform, earnings]) => ({ platform, earnings }))
.sort((a, b) => b.earnings - a.earnings)
.slice(0, 10); // Get top 10 platforms
setPlatformsData(sortedPlatforms);
};
return (
<View >
<CartesianChart
xKey={'platform' as never}
padding={5}
yKeys={['earnings'] as never}
domainPadding={{ left: 50, right: 50, top: 0, bottom: 0 }}
frame={{
lineWidth: { top: 0, left: 0, right: 1, bottom: 0 },
lineColor: 'white',
}}
axisOptions={{
font: font,
formatYLabel: (value: any) => `${abbreviateNumber(value, 3)}`,
formatXLabel: (value: any) => ``,
lineWidth: { grid: { x: 0.2, y: 0.2 }, frame: 0 },
lineColor: '#d4d4d8',
labelColor: 'white',
labelOffset: { x: 10, y: 10 },
axisSide: { x: 'bottom', y: 'right' },
}}
data={platformData as any}
>
{({ points, chartBounds }: any) => {
return points?.earnings?.map((item: any, index: any) => {
return (
<>
<Bar
points={[item]}
chartBounds={chartBounds}
animate={{ type: 'spring' }}
color={getColor(item?.xValue)}
barWidth={20}
roundedCorners={{
topLeft: 3,
topRight: 3,
}}
/>
</>
);
});
}}
</CartesianChart>
</View>
);
};```
I am using victory-native
TypeScript icon, indicating that this package has built-in type declarations
41.16.0
I am receiving the Error: Exception in HostFunction: Value is undefined, expected a number error when trying to render the chart. The platformData is populated with the calculated earnings values, but it seems like one or more of them might be undefined, causing the error.
What I’ve tried:
I’ve confirmed that platformData is correctly populated with the expected values.
I added checks in the calculatePlatformTotals function to ensure the earnings are valid numbers before adding them to platformTotals.
I quess issue is with rendring chart. When Initially map loads you send empty arry and chart is not able to find values.
use wrpper of the data like that
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text style={{color: '#FFFFFF', fontSize: 14}}>
Loading chart data...
</Text>
</View>
) : platformData.length === 0 ? (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text style={{color: '#FFFFFF', fontSize: 14}}>
No yearly data available to display chart.
</Text>
</View>
) : (
<CartesianChart
xKey={'platform' as never}
padding={5}
yKeys={['earnings'] as never}
domainPadding={{left: 50, right: 50, top: 0, bottom: 0}}
frame={{
lineWidth: {top: 0, left: 0, right: 1, bottom: 0},
lineColor: 'white',
}}
axisOptions={{
font: font,
formatYLabel: (value: any) => `${abbreviateNumber(value, 3)}`,
formatXLabel: (value: any) => ``,
lineWidth: {grid: {x: 0.2, y: 0.2}, frame: 0},
lineColor: '#d4d4d8',
labelColor: 'white',
labelOffset: {x: 10, y: 10},
axisSide: {x: 'bottom', y: 'right'},
}}
data={platformData as any}
chartPressState={state as any}
renderOutside={({chartBounds, xScale, yScale}) => {
const imageElements = platformData.map(
(item: any, index: number) => {
const currentImage = getImageurl(item?.platform);
if (!currentImage) return null;
const xPosition = xScale(index);
const yPosition = chartBounds.bottom + 10; // 10px below the chart
return (
<Image
key={`x-axis-image-${index}`}
image={currentImage}
x={xPosition} // Base X position
y={yPosition - 10} // Base Y position
width={20}
height={20}
transform={[
{translateX: xPosition + 10},
{translateY: yPosition + 10},
{rotate: 1.5 * Math.PI},
{translateX: -(xPosition + 10)},
{translateY: -(yPosition + 10)},
]}
/>
);
},
);
const yAxisLabels = platformData.map((item: any, index: number) => {
const xPosition = chartBounds.left - 30; // Adjust position for Y-axis labels
const yPosition = yScale(item.earnings);
return (
<SKText
key={`y-axis-label-${index}`}
font={font}
x={xPosition} // Left side of the chart
y={yPosition} // Align to Y-axis
text={`${item?.earnings}`}
color={'white'}
//transform={`rotate(-90, ${xPosition}, ${yPosition})`} // Rotate 90° counterclockwise
/>
);
});
return (
<>
{imageElements}
</>
);
}}>
{({points, chartBounds}: any) => {
return points?.earnings?.map((item: any, index: any) => {
return (
<>
<Bar
points={[item]}
chartBounds={chartBounds}
animate={{type: 'spring'}}
color={getColor(item?.xValue)}
barWidth={20}
roundedCorners={{
topLeft: 3,
topRight: 3,
}}
/>
</>
);
});
}}
</CartesianChart>
)}````