Search code examples
javascriptreactjsfusionchartsreact-fusioncharts

How to prevent a Map from re-rendering when a state variable changes (React) (Fusion Charts)


I have this problem where every time a user hovers there mouse pointer over my map of the USA, the map renders again. (Visually, it flashes on the screen and it looks terrible).

This is because I have an 'entityRolloever' event which causes a state variable to change.

      entityRollover: function(event, data){
        setHoveredState(data.label)
      }

How I understand it is, when a React component (i.e. my map) changes state, it re-renders.

But what I want is, when a user hovers over a U.S. State, I want the state that is being hovered over to show up in a separate text box (without the whole map re-rendering).

I have searched around, and I have found some other React hooks which could solve this. They are useRef, useMemo, and useCallback. However, I'm not sure how they would solve my problem as I have never used them before.

If you want to recreate my problem, create a basic React app

npx create-react-app

Install the following packages

npm i fusioncharts react-fusioncharts fusionmaps

Then grab the code in my jsfiddle and paste it into 'App.js'

https://jsfiddle.net/cmascardo5/s8trame5/


Solution

  • There are a couple of ways to resolve this issue. I chose to replace useState() hook with useRef() since it matches your case perfectly.

    useState() is a hook used in functional components to re-render components on state change, but in your case this is NOT something that we want. We want to track the updates without re-rendering the components. To achieve this, we use useRef(). When we use useRef() for the updates it will not fire re-rendering unlike useState().

    In your code, I commented out the {hoveredState} you had to avoid re-rendering. Then I created a p element and ref it with useRef(). Then on the entityRollove method, I update the innerText of the referenced p element. This way we are tracking the updates without re-rendering. It is simpler to see the code, I did comment on the lines I added: (paste it in your App.js)

    import React from 'react';
    import './App.css';
    
    import ReactFC from 'react-fusioncharts';
    import FusionCharts from 'fusioncharts';
    import FusionMaps from 'fusioncharts/fusioncharts.maps';
    import USA from 'fusionmaps/maps/fusioncharts.usa';
    import FusionTheme from 'fusioncharts/themes/fusioncharts.theme.fusion';
    import { Grid, Paper } from '@material-ui/core';
    import { useState } from 'react';
    import { useRef } from 'react';
    
    ReactFC.fcRoot(FusionCharts, FusionMaps, USA, FusionTheme);
    
    export default function App({}) {
      const [hoveredState, setHoveredState] = useState('All States');
      let test1 = 'test';
      // const hoveredState =useRef("All states");
    
      // Here we are using UseRef() to get a reference to an element we want to use to show each State's names
      const paragraphEl = useRef(null);
    
      const colorrange = {
        minvalue: '20',
        code: '#00A971',
        gradient: '1',
        color: [
          {
            minvalue: '20',
            maxvalue: '40',
            code: '#EFD951',
          },
          {
            minvalue: '40',
            maxvalue: '60',
            code: '#FD8963',
          },
          {
            minvalue: '60',
            maxvalue: '80',
            code: '#D60100',
          },
        ],
      };
      const data = [
        {
          id: 'HI',
          value: '70.0',
        },
        {
          id: 'DC',
          value: '52.3',
        },
        {
          id: 'MD',
          value: '54.2',
        },
        {
          id: 'DE',
          value: '55.3',
        },
        {
          id: 'RI',
          value: '50.1',
        },
        {
          id: 'WA',
          value: '48.3',
        },
        {
          id: 'OR',
          value: '48.4',
        },
        {
          id: 'CA',
          value: '59.4',
        },
        {
          id: 'AK',
          value: '26.6',
        },
        {
          id: 'ID',
          value: '44.4',
        },
        {
          id: 'NV',
          value: '49.9',
        },
        {
          id: 'AZ',
          value: '60.3',
        },
        {
          id: 'MT',
          value: '42.7',
        },
        {
          id: 'WY',
          value: '42.0',
        },
        {
          id: 'UT',
          value: '48.6',
        },
        {
          id: 'CO',
          value: '45.1',
        },
        {
          id: 'NM',
          value: '53.4',
        },
        {
          id: 'ND',
          value: '40.4',
        },
        {
          id: 'SD',
          value: '45.2',
        },
        {
          id: 'NE',
          value: '48.8',
        },
        {
          id: 'KS',
          value: '54.3',
        },
        {
          id: 'OK',
          value: '59.6',
        },
        {
          id: 'TX',
          value: '64.8',
        },
        {
          id: 'MN',
          value: '41.2',
        },
        {
          id: 'IA',
          value: '47.8',
        },
        {
          id: 'MO',
          value: '54.5',
        },
        {
          id: 'AR',
          value: '60.4',
        },
        {
          id: 'LA',
          value: '66.4',
        },
        {
          id: 'WI',
          value: '43.1',
        },
        {
          id: 'IL',
          value: '51.8',
        },
        {
          id: 'KY',
          value: '55.6',
        },
        {
          id: 'TN',
          value: '57.6',
        },
        {
          id: 'MS',
          value: '63.4',
        },
        {
          id: 'AL',
          value: '62.8',
        },
        {
          id: 'GA',
          value: '63.5',
        },
        {
          id: 'MI',
          value: '44.4',
        },
        {
          id: 'IN',
          value: '51.7',
        },
        {
          id: 'OH',
          value: '50.7',
        },
        {
          id: 'PA',
          value: '48.8',
        },
        {
          id: 'NY',
          value: '45.4',
        },
        {
          id: 'VT',
          value: '42.9',
        },
        {
          id: 'NH',
          value: '43.8',
        },
        {
          id: 'ME',
          value: '41.0',
        },
        {
          id: 'MA',
          value: '47.9',
        },
        {
          id: 'CT',
          value: '49.0',
        },
        {
          id: 'NJ',
          value: '52.7',
        },
        {
          id: 'WV',
          value: '51.8',
        },
        {
          id: 'VA',
          value: '55.1',
        },
        {
          id: 'NC',
          value: '59.0',
        },
        {
          id: 'SC',
          value: '62.4',
        },
        {
          id: 'FL',
          value: '70.7',
        },
      ];
      // };
      FusionCharts.ready(function () {
        var chart = new FusionCharts({
          type: 'maps/usa',
          caption: 'Average Temperature of US States',
          subcaption: '1979 - 2000',
          entityfillhovercolor: '#F8F8E9',
          numbersuffix: '°F',
          showlabels: '1',
          borderthickness: '0.4',
          theme: 'fusion',
          entitytooltext:
            '<b>$lname</b> has an average temperature of <b>$datavalue</b>',
          renderAt: 'chart-container',
          width: '700',
          height: '500',
          colorrange: colorrange,
          dataFormat: 'json',
          dataSource: {
            chart: {
              caption: 'Average Temperature of US States',
              subcaption: '1979 - 2000',
              entityfillhovercolor: '#F8F8E9',
              numbersuffix: '°F',
              showlabels: '1',
              borderthickness: '0.4',
              theme: 'fusion',
              entitytooltext:
                '<b>$lname</b> has an average temperature of <b>$datavalue</b>',
              // "animation":0,
              // "defaultAnimation":0
            },
            colorrange: colorrange,
            data: data,
          },
          events: {
            preRender: function (event, data) {
              // code to be executed before the chart is re-rendered
            },
            postRender: function (event, data) {
              // code to be executed after the chart is re-rendered
            },
            entityRollover: function (event, data) {
              console.log('test..' + data.label);
              // Here we get a reference to the paragraph element and set its innerText to be the state's name we are hovering on
              paragraphEl.current.innerText = data.label;
            },
          },
        }).render();
      });
      return (
        <Grid container spacing={2}>
          <Grid item xs={9}>
            <div id="chart-container"></div>
          </Grid>
          <Grid item xs={3}>
            // we create a p element that will hold state's name on hover
            <p ref={paragraphEl}></p>
            {/* {hoveredState} */}
          </Grid>
        </Grid>
      );
    }