recharts: When updating data dynamically, Axis is not updated

I used websocket to dynamically update state, and now the curve has changed, but the value on the axis is unchanged

I want the chart to show changes with the state changes, is there any way?

version: 0.22.3

import React, { Component } from 'react';
import { ComposedChart, Line, Area, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';

export default class extends Component {
  static defaultProps = {
    chartData: [],
  };

  state = {
    chartData: this.props.chartData,
  }

  componentWillReceiveProps({ chartData }) {
    if (this.props.chartData !== chartData) {
      this.setState({ chartData });
    }
  }

  render() {
    const { chartData } = this.state;
    return (
      <div>
        {
          chartData.length ? <ResponsiveContainer height={250} >
            <ComposedChart data={chartData} margin={{ top: 0, right: -15, left: -8, bottom: 0 }} >
              <XAxis dataKey="Name" />
              <YAxis yAxisId="left" orientation="left" />
              <YAxis yAxisId="right" orientation="right" />
              <CartesianGrid strokeDasharray="3 3" />
              <Tooltip />
              <Legend />
              <Area yAxisId='left' type='monotone'  dataKey='TotalCount' fill={'#00BCD4'} stroke={'#00BCD4'}  />
              <Line yAxisId='right' type='monotone'  dataKey='AverageElapsed' stroke={'#00E676'} unit='ms' />
              <Bar yAxisId='right' type='monotone'  dataKey='TotalErrors' fill={'#F44336'}  />
            </ComposedChart>
          </ResponsiveContainer> : <div className='chart-empty'>empty</div>
        }
      </div>
    )
  }
}

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 17 (2 by maintainers)

Commits related to this issue

Most upvoted comments

The problem for me was that the API returned the numbers in a string format which confused Recharts.

It’s still an issues on version 1.8.1

No plan to fix this issue?

workaround to pass random key to force rerender…

    <YAxis
          key={Math.random()}
          orientation="right"
          yAxisId="y2"
    />

Version: 1.0.0-alpha.2 I don’t know if this is the right place to ask but I’m too facing a similar problem. In my case I’m changing a particular data dynamically but the chart is not getting updated. The tooltips show the updated value. But not the chart. Here’s the part that I have used:

                        <ResponsiveContainer width='100%' height={this.props.height}>
                            <BarChart
                                data={this.props.data}
                                margin={{ top: 0, right: 0, bottom: 0, left: 0 }}
                            >
                                <XAxis dataKey="name" />
                                <YAxis dataKey="value" />
                                {
                                    this.props.showGrid &&
                                    <CartesianGrid strokeDasharray="3 3" />
                                }
                                {
                                    this.props.showTooltip &&
                                    <Tooltip />
                                }
                                {
                                    this.props.showLegend &&
                                    <Legend />
                                }
                                <Bar dataKey="value" fill="red" />
                            </BarChart>
                        </ResponsiveContainer>

this.props.data is of the form: [{name: 'Something', value: number}] These are the PropTypes in case you need them:

ReBarChart.propTypes = {
    data: PropTypes.arrayOf(PropTypes.shape({
        name: PropTypes.string.isRequired,
        value: PropTypes.number.isRequired
    })),
    legend: PropTypes.string.isRequired,
    height: PropTypes.number,
    showTooltip: PropTypes.bool,
    showLegend: PropTypes.bool,
    showGrid: PropTypes.bool,

    displayTitle: PropTypes.bool,
    titleValue: PropTypes.string,
    titleSize: PropTypes.number,
    subTitleValue: PropTypes.string,
    subTitleSize: PropTypes.number,
};

Here’s the function that I used to update the data:

setRandomBarChartData() {
        console.log("Button Clicked");
        let index = Math.floor(Math.random() * this.state.barChartData.data.length);
        let randomValue = Math.floor(Math.random() * 100);

        let barChartData = { ...this.state.barChartData };
        barChartData.data[index].value = randomValue;
        console.log("Bar Chart Data: ", barChartData);

        this.setState({ barChartData: barChartData });
}

The data generated through this is being passed as a prop to the component.

My data was mapped from an object that already had an id. Setting ResponsiveContainer.key to the id solved it for me.

My solution:

const [renderCount, forceUpdate] = useReducer(x => x + 1, 0);

useLayoutEffect(() => {
  forceUpdate(); // Call forceUpdate when data changes
}, [data, forceUpdate]);

return (
  <PieChart key={renderCount}>
    {/* ... */}
  </PieChart>
);

@xiaoyu-tamu , yes! That’s the solution that worked.

const { varFromState } = this.state
<ResponsiveContainer width='90%' height={400} key={varFromState}>
  <BarChart
    data={chartData}
    key={varFromState}
  >
    <CartesianGrid strokeDasharray='3 3' />
    <XAxis dataKey='name' />
    <YAxis key={varFromState} />
    <Tooltip />
    <Legend />
    <Bar dataKey='value' fill='#8884d8' />
  </BarChart>
</ResponsiveContainer>

I ended up just passing the value from the state variable to the key prop all along the tree and it worked. Thank you for your help and suggestion, hopefully this is also useful to others.

@joaocarmo, I noticed that pass random key to axis will cause some weird behavior.

Try this. Business logic removed, the idea is force chart to re-render when chartData change.

const twoYearsAgo = subYears(new Date(), 2);

const AdminOverviewsPage = props => {
  const classes = useStyles();
  const router = useRouter();
  const [chartKey, setChartKey] = React.useState(0);
  const range = String(router.query.range || "daily");

  const { data, loading, error } = useSelectDailyBusinessReportQuery({
    variables: { where: { date: { _gte: twoYearsAgo } } }
  });

  const chartData = React.useMemo(() => {
    const value = data && data.business_report_daily;
    // update key to force chart re-render
    setChartKey(Math.random());

    if (!value) return [];
    if (range === "daily") return value.slice(value.length - 60, value.length);
    return Object.values(
      value.reduce(
        (acc, { date, ...curr }) => {
          if (acc[key]) {
          } else {
          }
          return acc;
        },
        {} as Record<string, any>
      )
    );
  }, [data, range]);

  if (loading) return <div>loading</div>;
  if (!chartData || error) return <div>error</div>;
  return (
      <Container>
          <Button
             onClick={() => {
              router.push("/admin/overviews?range=monthly");
            }}
          >
            Monthly
          </Button>
          <Button
             onClick={() => {
              router.push("/admin/overviews?range=daily");
            }}
          >
            Daily
          </Button>
        // chartKey updated when data changed
        <ListingSessionChart data={chartData} key={chartKey} />
        <ListingSalesChart data={chartData} key={chartKey} />
      </Container>
  );
};

where ListingSessionChart and ListingSalesChart are SFCs, something like this 
<ResponsiveContainer>
  <AnyChart data={data}>
    ...
  </AnyChart>
</ResponsiveContainer>

I’m having exactly the same issue as Rud156, tooltip gets update but chart doesn’t, the only way I was able to fix this was by setting the data in my state as [] and then updating it again with the new state.