Abhinandan-Kushwaha / react-native-gifted-charts

The most complete library for Bar, Line, Area, Pie, Donut, Stacked Bar and Population Pyramid charts in React Native. Allows 2D, 3D, gradient, animations and live data updates.
https://www.npmjs.com/package/react-native-gifted-charts
MIT License
701 stars 147 forks source link

[Android] Bar chart renders incorrectly when data changes multiple times #643

Closed Justin-Terry closed 1 month ago

Justin-Terry commented 3 months ago

Libraries Installed

"react-native-gifted-charts": "1.4.8",
"react-native-svg": "15.2.0",
"expo-linear-gradient": "~13.0.2",
"react-native-linear-gradient": "*"


Explanation

If the data in a bar chart is changed (one to several times) the chart begins to render the data incorrectly. When the chart is animated it takes several changes to surface the bug but the chart begins rendering some bars the wrong color and/or doesn't render them at all. If the chart is not animated then old data will remain on the chart.

The issue originally occurred for me using an Expo Dev Client build but the minimum reproduction proved it will also happen in Expo Go.

The same code works fine on iOS.


Screenshots

Android Android

iOS iOS


Reproduction Videos

Animated https://github.com/Abhinandan-Kushwaha/react-native-gifted-charts/assets/43148991/3c62f11f-67c1-4c31-ab9a-cc2d20b94011

Not Animated https://github.com/Abhinandan-Kushwaha/react-native-gifted-charts/assets/43148991/3631eb84-8fda-4e37-bdbc-e5c125730325

Same code on iOS behaving as expected https://github.com/Abhinandan-Kushwaha/react-native-gifted-charts/assets/43148991/9f2f2f58-bb9f-4358-bca3-624a08a65f03


Reproduction Steps

I have a repository here: https://github.com/Justin-Terry/minimum-charts-repro

If you'd prefer to do it yourself, this is the component.

import {FC, useState} from 'react'
import {
    Button, Dimensions,
    StyleSheet, View,
} from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'
import {BarChart} from "react-native-gifted-charts";

const BAR_SPACING = 3

const tempDayData = [
    { value: 0 },
    { value: 50 },
    { value: 100 },
    { value: 150 },
    { value: 200 },
    { value: 250 },
    { value: 300 },
    { value: 350 },
    { value: 400 },
    { value: 450 },
    { value: 500 },
    { value: 550 },
    { value: 600 },
    { value: 650 },
    { value: 700 },
    { value: 750 },
    { value: 800 },
    { value: 850 },
    { value: 900 },
    { value: 950 },
    { value: 1000 },
    { value: 1050 },
    { value: 1100 },
    { value: 1150 },
]

const tempWeekData = [
    { value: 0 },
    { value: 100 },
    { value: 200 },
    { value: 300 },
    { value: 400 },
    { value: 500 },
    { value: 600 },
]

const tempMonthData = [
    { value: 0 },
    { value: 35 },
    { value: 70 },
    { value: 105 },
    { value: 140 },
    { value: 175 },
    { value: 210 },
    { value: 245 },
    { value: 280 },
    { value: 315 },
    { value: 350 },
    { value: 385 },
    { value: 420 },
    { value: 455 },
    { value: 490 },
    { value: 525 },
    { value: 560 },
    { value: 595 },
    { value: 630 },
    { value: 665 },
    { value: 700 },
    { value: 735 },
    { value: 770 },
    { value: 805 },
    { value: 840 },
    { value: 875 },
    { value: 910 },
    { value: 945 },
    { value: 980 },
    { value: 1015 },
    { value: 1050 },
]

interface HomeScreenProps {}

const HomeScreen: FC<HomeScreenProps> = () => {
    const [data, setData] = useState(tempDayData)
    const [chartWidth, setChartWidth] = useState(Dimensions.get('screen').width)

    return (
        <SafeAreaView style={styles.page} edges={['top']}>
                <View style={styles.buttonWrapper}>
                    <Button
                        title={'DAY'}
                        onPress={() => setData(tempDayData)}
                    />
                    <Button
                        title={'WEEK'}
                        onPress={() => setData(tempWeekData)}
                    />
                    <Button
                        title={'MONTH'}
                        onPress={() => setData(tempMonthData)}
                    />
                </View>
                <BarChart
                    isAnimated={true}
                    animationDuration={500}
                    data={data}
                    width={chartWidth}
                    spacing={BAR_SPACING}
                    barWidth={8}
                    height={chartWidth / 2}
                />
        </SafeAreaView>
    )
}

export default HomeScreen

const styles = StyleSheet.create({
        page: {
            flex: 1,
            backgroundColor: 'white'
        },
        buttonWrapper: {
            margin: 24,
            flexDirection: 'row',
            justifyContent: 'space-between'
        },
        pageContent: {
            flexGrow: 1,
            paddingTop: 24,
            paddingHorizontal: 16,
            alignItems: 'center',
        },
    })
RaheemWilson commented 3 months ago

@Justin-Terry a very hacky way to resolve this issue is to add a key prop to the bar chart and ensure that the value changes each time a click is made. For instance, the title for the buttons, that you have, could be used. The key prop triggers the bar chart to rerender each time its value changes.

Abhinandan-Kushwaha commented 1 month ago

Hi @Justin-Terry 👋 Thanks for reporting this bug.

The issue with Bars not re-rendering on data change in case of non-animated chart has been fixed in version 1.4.18

Please use the latest version of the library.