1

I am trying to build an employee org chart using Highcharts network graph but I'm running into errors. The idea is simple. I want to render an initial chart with the president and a few of their direct reports. After that, if the user clicks on a node in the chart, I want to pull data for that individual's direct reports and update the graph with the children nodes for that individual's subordinates. The back end API for the data pull is working fine

Here is my code:

import React, { useState, useEffect, useRef } from 'react';
import axios from 'axios';
import Highcharts from 'highcharts';
import HighchartsReact from 'highcharts-react-official';

import networkgraph from "highcharts/modules/networkgraph";
networkgraph(Highcharts);

const Employees3 = () => {

    const chartComponent = useRef(null); // reference to chart obj 
    const [nodeData, setNodeData] = useState([
        { 'id': 'A', dataLabels: { enabled: true }, marker: { radius: 11, fillColor: 'red' } },
        { 'id': 'P', dataLabels: { enabled: true } },
        { 'id': 'K', dataLabels: { enabled: true } },
        { 'id': 'S', dataLabels: { enabled: true } },
        { 'id': 'D', dataLabels: { enabled: true } },
    ]);

    const [linkData, setLinkData] = useState([
        { 'from': 'D', 'to': 'A' },
        { 'from': 'S', 'to': 'A' },
        { 'from': 'K', 'to': 'A' },
        { 'from': 'P', 'to': 'A' }
    ]);


    const [chartOptions, setChartOptions] = useState({
        chart: {
            type: 'networkgraph',
            plotBorderWidth: 1,
        },
        title: {
            text: 'Phrasal verbs'
        },
        subtitle: {
            text: 'Integration: ' + 'euler'
        },
        credits: false,
        plotOptions: {
            networkgraph: {
                layoutAlgorithm: {
                    enableSimulation: false,
                    integration: 'euler',
                    linkLength: 25,
                },
                keys: ['from', 'to'],
                marker: {
                    radius: 5,
                    lineWidth: 1
                }
            },
            series: {
                point: {
                    events: {
                        click: function () {
                            var name = this.id.replace(' ', '%20');
                            axios.get('api/employee_data/' + name)
                                .then(response => { 
                                    setNodeData(nodeData.concat(response.data.nodes));
                                    setLinkData(linkData.concat(response.data.links));
                                     chartComponent.current.chart.redraw();
                                }).catch( err => {
                                })
                            },
                    }
                }
            }
        },
        series: [{
            allowPointSelect: true,
            nodes: nodeData,
            data: linkData,
        }]
    });

    console.log('nodes: ', nodeData)
    console.log('links: ', linkData)
    
    return (
        <div>
            <HighchartsReact
                highcharts={Highcharts}
                options={chartOptions}
                containerProps={{ style: { height: 700 } }}
                allowChartUpdate = {true}
                ref={chartComponent}
            />
        </div>
    )
};

export default Employees3;

I'm using a function on the series.event.click object to render a GET call to pull the child nodes and links from the server and update my state for nodeData and linkData. When I do a console log of these state arrays, it is showing me the right data. I then try to rerender the chart object with the updated data using the redraw method on the chart reference. The problem is that the chart is not rerendering with the new data using this approach. Instead it continues to show the old data.

Any thoughts on what I may be doing wrong here and how I can rerender the chart after the data update?

Much appreciated

EDIT (2-6-21)

axios.get('api/employee_data/' + name)
    .then(response => {

        setNodeData((prevState) => {
            return [...prevState, ...response.data.nodes]
        });
        setLinkData((prevState) => {
            return [...prevState, ...response.data.links]
        });

        setChartOptions({
            series: [{
                nodes: nodeData, //doesnt work
                data: linkData //doesnt work
                nodes: response.data.nodes,  // works but doesn't give me what I need
                data: response.data.links,
            }]
        });
    });
jay queue
  • 285
  • 2
  • 14

1 Answers1

1

You need to update state which is directly related with a chart component options props:

    plotOptions: {
        ...,
        series: {
            point: {
                events: {
                    click: function () {
                        var name = this.id.replace(' ', '%20');
                        axios.get('api/employee_data/' + name)
                            .then(response => { 
                                setChartOptions({
                                    series: [{
                                        nodes: response.data.nodes,
                                        data: response.data.links
                                    }]
                                });
                            }).catch( err => {})
                        }
                }
            }
        }
    }

Live demo: https://codesandbox.io/s/highcharts-react-demo-ngq7w?file=/demo.jsx

ppotaczek
  • 36,341
  • 2
  • 14
  • 24
  • Thanks for your example, very helpful. I’m still running into an issue though. See code edits above. Basically , if I were to use setChartOptions with the response data from the api call directly as you said, then the chart correctly updates, however, if I use the api response data to update the nodeData and linkData state variables and try to pass those to the setChartOptions as I’ve shown, then it doesn’t work. I need to update the state variables because a user can click multiple times to build out the network. Any ideas on why this isn’t working? Could it have to do with state scheduling? – jay queue Jun 03 '21 at 03:33
  • following back up on this. Thoughts on why this may not work? Thx – jay queue Jun 03 '21 at 14:34
  • 1
    Hi @jay queue, Yes, it is related with async state update. You should not use nested state and you should be able to achieve the wanted result by using only one state: `nodes: [...prevOptions.series[0].nodes, ...data.nodes]` Example: https://codesandbox.io/s/highcharts-react-demo-7801i?file=/demo.jsx – ppotaczek Jun 04 '21 at 12:52