typescriptreact-nativevictory-charts

React native victory chart gives error value undefined


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.


Solution

  • 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>
       )}````