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>
`;
}
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.
POINT#4: Props in DonutChart was updated, but callback wasn't and 'case 1' is still showing loader.
Can't understand why it happens.
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