react-apexcharts
react-apexcharts copied to clipboard
Synced Charts are getting too many Options overwritten.
I have multiple <ApexChart>
's that share a group
in order to synchronize the tooltips.
When I hover over "MAX(value)", they'll all get the same title
I tracked it down to multiple issues.
1. Something in apexcharts.js is modifying ApexCharts.props.options
directly.
On the 2nd render of ApexCharts
, this.chart.updateOptions(this.getConfig())
is being called because the prevOptions !== currentOptions
https://github.com/apexcharts/react-apexcharts/blob/7345fdca622358f181aa64e1cdfc76f592f4bdf0/src/react-apexcharts.jsx#L116
When I pause the debugger, something has added xaxis.convertedCatToNumeric: false
to my props.options
To get around this you should probably:
A. Do a deep clone of options
in getConfig()
.
B. Deep clone options
in apexcharts.js
.
C. Stop modifying options
in apexcharts.js
2. _updateOptions
in apexcharts.js shouldn't update so many options
for Synced Charts
https://github.com/apexcharts/apexcharts.js/blob/433fccce57a1dfd3ff1b67a731818189146ca7ca/src/modules/helpers/UpdateHelpers.js#L63
What's happening in my example, is you're taking the options
for the bottom chart and applying it to all the charts (minus the series
).
I'm not sure the reasoning for doing this, but options like title.text
definitely should not be synced, and I imagine many others.
I am having this issue too.
On the initial render everything looks great, but React re-renders when I scroll and then all my options are sync'd across the multiple graphs which means colours, axis labels and everything become the same.
Ok I worked out how to fix this for my example, unsure if it is the same thing that is happening to you.
I wasn't storing the graph options in React State. I had a generator function outputting the options.
Unsure why the behaviour didn't allow for this to work after the initial render...
Anyway, moving this logic into a useEffect
where I set an object to have the state output from the generator it stopped the issue completely.
Ok I worked out how to fix this for my example, unsure if it is the same thing that is happening to you.
I wasn't storing the graph options in React State. I had a generator function outputting the options. Unsure why the behaviour didn't allow for this to work after the initial render... Anyway, moving this logic into a
useEffect
where I set an object to have the state output from the generator it stopped the issue completely.
@cstrat I tried your approach to pass the option state set in useEffect but still same issue after react re-renders. Am I missing something maybe? could you create a small codebox example if possible?
I think that @cstrat likely made it so that Apex is mutating React state now, so the mutation persists between renders.
I can also confirm that under the hood Apex core mutates the options.xAxis.type
to numeric
. In react devtools, it "looks" like we are passing this prop even though we never did!
It turns out Apex actually mutates the options
, which triggers your wrapper to update and runs componentDidUpdate
with the "numeric" axis, which blocks the page as all of our linked charts re-render, and makes them look corrupt since they are not intended to visualize on "numeric" scale.
On our end we pass a key
prop to the charts which changes together with the dataset. We also copy pasted your source code into our repo and commented out the entire componentDidUpdate
, basically forcing fresh unmount and mounts every time. We had to pass the key
to force remounts on our end, and also disable componentDidUpdate
in this library's code in order to prevent the updates triggered when Apex erroneously mutates the props.
This is related to https://github.com/apexcharts/apexcharts.js/issues/914 and https://github.com/apexcharts/apexcharts.js/issues/623
Also confirming that simply deep cloning in getConfig() solves the problem as @cdeutsch suggests. This should be an easy PR
Easy PR and 3 years late, the problem is still here. At this point, I think I am diching apexchart...
The easiest solution I have managed to work is just simply extract all options into separate variable:
var options = { colors: ['#fff'], chart: { id: 'earnings', group: 'results', type: 'area', foreColor: '#fff', zoom: { enabled: false, }, }, dataLabels: { enabled: false, }, stroke: { curve: 'smooth', width: 3, }, tooltip: { enabled: true, shared: false, theme: 'dark', x: { show: false, format: 'ddd' }, y: { formatter: (value) => ``${value} $``, }, }, grid: { show: false, xaxis: { lines: { show: false, }, }, yaxis: { lines: { show: false, }, }, }, xaxis: { type: 'datetime', axisBorder: { show: false, }, axisTicks: { show: false, }, labels: { show: false, }, }, yaxis: { show: false, opposite: true, }, };
And then make new state to hold it:
const [chartOptions, setChartOptions] = useState(options);
Later on just pass it to your render Component:
<ApexCharts options={chartOptions} series={series} type='line' height={100} key={0} />
In my case it worked perfectly and I had to make this only on the one of my synced charts.
@kamzak thanks for sharing, however lots of us actually do need the chart options to be dynamically updated. Im not sure why the state is necessary since you dont appear to ever update it. The bug occurs when updating the options (it wouldnt matter how the initial state is stored or whether it came from state vs props, that is not how react works)
@kamzak your solution works for me, but apexcharts should fix this issue.
The easiest solution I have managed to work is just simply extract all options into separate variable:
var options = { colors: ['#fff'], chart: { id: 'earnings', group: 'results', type: 'area', foreColor: '#fff', zoom: { enabled: false, }, }, dataLabels: { enabled: false, }, stroke: { curve: 'smooth', width: 3, }, tooltip: { enabled: true, shared: false, theme: 'dark', x: { show: false, format: 'ddd' }, y: { formatter: (value) =>
${value} $, }, }, grid: { show: false, xaxis: { lines: { show: false, }, }, yaxis: { lines: { show: false, }, }, }, xaxis: { type: 'datetime', axisBorder: { show: false, }, axisTicks: { show: false, }, labels: { show: false, }, }, yaxis: { show: false, opposite: true, }, };
And then make new state to hold it:
const [chartOptions, setChartOptions] = useState(options);
Later on just pass it to your render Component:
<ApexCharts options={chartOptions} series={series} type='line' height={100} key={0} />
In my case it worked perfectly and I had to make this only on the one of my synced charts.
This one worked, but they neeed to fix this