reactjshighchartsreact-highcharts

Why callback is not called after prop was updated?


I'm trying to implement loader with async behavior.

I have a code:

import { DonutChart } from './donut-chart';
const ParentComponent = () => {

    const [isLoading, setIsLoading] = useState(true); // POINT#1

    useEffect(() => {
        // .... some logic to call API and load some data for a chart
        setIsLoading(false) // POINT#2
    }, []);

    return <DonutChart isLoading={isLoading} {...args} />
}

// donut-chart.jsx

// import ... from modules
highchartsMore(Highcharts);
addExportingModule(Highcharts);
addExportData(Highcharts);
accessibilityModule(Highcharts);
noDataModule(Highcharts);

const DonutChart = ({ isLoading, ...rest}) => {
const options = {...}; // some options from Highcharts API

console.log("POINT#3", isLoading);

return (
    <HighchartsReact
        highcharts={Highcharts}
        options={options}
        update={isLoading}
        callback={instance => { // POINT#4: Here could be the problem
            if (isLoading) {
                console.log('case 1', isLoading);
                instance.showLoading(getCustomLoader());
            } else {
                console.log('case 2', isLoading);
                instance.hideLoading();
            }
        }}
    />
)

getCustomLoader - is function for injecting some custom styles to animate loader. In our case this is a circle as in Material

const getCustomLoader = () => {
    const hasStyles = document.getElementById('custom-loader-styles');
    const innerStyles ='some styles'
    const element = hasStyles || document.createElement('style');

    element.setAttribute('id', 'custom-loader-styles');
    element.innerHTML = innerStyles;
    document.head.appendChild(element);

      return `
      <svg class="custom-loader" viewBox="22 22 44 44">
          <circle class="custom-loader__circle" cx="44" cy="44" r="20.2" fill="none" stroke-width="3.6"></circle>
      </svg>
  `;
}

Expected behaviour

POINT#1: isLoading === true, console.log shows in POINT#3 sows true, loader is visible
POINT#2: setIsLoading was called and isLoading becames 'false'
POINT#3: console.log displays: "POINT#3", false
POINT#4: Props in DonutChart should be updated, and callback should be updated as well. And in callback I 'case 2' should hide loader.

Actual behaviour

POINT#4: Props in DonutChart was updated, but callback wasn't and 'case 1' is still showing loader.

Can't understand why it happens.


Solution

  • That is intended behaviour. If immutable option is disabled, a callback is fired only once. In Highcharts API we can read:

    Function to run when the chart has loaded and all external images are loaded. Defining a chart.events.load handler is equivalent.

    Use useEffect hook or render chart event to handle the loader.

    useEffect(() => {
      // handle the loader
    }, [isLoading]);
    

    Live example: https://codesandbox.io/p/sandbox/highcharts-react-demo-forked-z25gj6

    API Reference:

    https://api.highcharts.com/class-reference/Highcharts.Chart#Chart

    https://api.highcharts.com/highcharts/chart.events.redraw

    Docs: https://github.com/highcharts/highcharts-react#options-details