0

I am trying to show bing maps on my React Application using props. The Maps are properly displayed on load but when the props change it will not reload the map component. I know the issue is componentDidMount() will be called only once when the component loads but I am not sure how to resolve it.

interface IState {
    addresses: Array<PropertyDetails>;
}

interface PropertyDetails {
    fullAddress: string;
    propertyNumber: number
}

export class Map extends React.Component<{ data }, IState> {

constructor(props) {
    super();
    this.state = {
        addresses: []
    };
}

getPropertyData() {
    let propertyData: Array<PropertyDetails> = [];
    let listProperties = this.props.data;

    if (listProperties.length > 0) {
        for (let i = 0; i < listProperties.length; i++) {
            let address = listProperties[i].Streetnumber + " " + 
                          listProperties[i].Streetname + " " + 
                          listProperties[i].City + " " + 
                          listProperties[i].State + " " + 
                          listProperties[i].Zip; 

            var Property: PropertyDetails = {
                fullAddress: address,
                propertyNumber: listProperties[i].Propertyidentity
            };

            propertyData.push(Property);
        }
    }

    this.setState({ addresses: propertyData })
}

loadBingMapScript() {
    var BingMaps = document.getElementById("BingMaps");
    if (BingMaps) {
        document.body.removeChild(BingMaps);
    }

    (window as any).loadMapScenario = () => this.InitMap();

    const script = document.createElement("script");
    script.src = "https://www.bing.com/maps/sdk/mapcontrol?callback=loadMapScenario";

    script.async = true;
    script.defer = true;

    script.id = "BingMaps";
    document.body.appendChild(script);
}

componentDidMount() {
    this.getPropertyData();
    this.loadBingMapScript();
}

private InitMap(): void {
    let mapElement: HTMLElement = this.refs.map as HTMLElement;
    (window as any).ShowMap(mapElement, this.state.addresses);
}

public render() {
    return <div>
        <div style={{ height: "500px", width: "100%" }}>
            <div id="map" ref="map"></div>
        </div>
    </div>
}
}

In the above code I am loading Bing Maps script in componentDidMount() and it calls a JS Method in Index.html. But when the second set of props are passed, It doesn't load the component again. So Maps doesn't refresh.

Below is index.html

function ShowMap(div, AddressList) {

        var map = new Microsoft.Maps.Map(div, {
            credentials: 'Key'

        });

        map.setView({
            mapTypeId: Microsoft.Maps.MapTypeId.canvasLight,
            center: new Microsoft.Maps.Location(39.828605, -98.579501),
            zoom: 4,
            customMapStyle: {
                elements: {
                    area: { fillColor: '#b6e591' },
                    water: { fillColor: '#75cff0' },
                    tollRoad: { fillColor: '#a964f4', strokeColor: '#a964f4' },
                    arterialRoad: { fillColor: '#ffffff', strokeColor: '#d7dae7' },
                    road: { fillColor: '#ffa35a', strokeColor: '#ff9c4f' },
                    street: { fillColor: '#ffffff', strokeColor: '#ffffff' },
                    transit: { fillColor: '#000000' }
                },
                settings: {
                    landColor: '#efe9e1'
                }
            },
        });

        SearchMap(map, AddressList)

    }

    function SearchMap(map, addresses) {
        for (let i = 0; i < addresses.length; i++) {
            Microsoft.Maps.loadModule('Microsoft.Maps.Search', function () {
                var searchManager = new Microsoft.Maps.Search.SearchManager(map);
                var requestOptions = {
                    where: addresses[i].fullAddress,
                    callback: function (answer, userData) {
                        map.entities.push(new Microsoft.Maps.Pushpin(answer.results[0].location));
                    }
                };
                searchManager.geocode(requestOptions);
            });
        }
    }

UPDATE I find loading the Maps again in ComponentDidUpdate() did somewhat fixed my issue but endedup in getting so many errors. So I am using React-Bing Maps npm package.

Teja Swaroop
  • 185
  • 1
  • 12
  • It seems there's nothing from state being rendered, so the component has no reason to update on state change. Try adding `key={this.state.addresses.length}` to the div containing what you want rerendered to see if that's the problem. If it is, you'll need a better key than that--that'll just rerender it every time the length of the addresses array changes. – Ted Mar 05 '19 at 21:03
  • It does't change because state doesn't change when props change. My props have data but I am not sure how my components refreshes when it have the new props. – Teja Swaroop Mar 05 '19 at 21:14

2 Answers2

1

I would you use this approach

componentDidUpdate (prevProps) {
    if (this.props !== prevProps) {
     // you can call your function here
     // Try to not use setState in this area
     // If you need to use, make sure to change only once
     // using if conditions like if(this.state.once)... then set to false
     }
}

This solution kind of worked. Its updating the map but I also have an issue.

 componentDidUpdate(prevProps) {
    if (this.props !== prevProps) {
        this.getPropertyData();
        this.loadBingMapScript();
    }
}

I Have Added the above code and I am getting following errors.

VM368 Log:1 Uncaught TypeError: Microsoft.Maps.NetworkCallbacks.f_logCallbackRequest is not a function
    at VM100 Log:1
(anonymous) @ VM368 Log:1
3mapcontrol?callback=loadMapScenario:16 Uncaught TypeError: Cannot read property '0' of null
Teja Swaroop
  • 185
  • 1
  • 12
  • 1
    This solution kind of worked. Its updating the map but I also have an issue. componentDidUpdate(prevProps) { if (this.props !== prevProps) { this.getPropertyData(); this.loadBingMapScript(); } } I Have Added the above code and I am getting following errors. VM368 Log:1 Uncaught TypeError: Microsoft.Maps.NetworkCallbacks.f_logCallbackRequest is not a function at VM100 Log:1 (anonymous) @ VM368 Log:1 3mapcontrol?callback=loadMapScenario:16 Uncaught TypeError: Cannot read property '0' of null – Teja Swaroop Mar 06 '19 at 16:57
  • I see. Did you find a solution for these Errors? I'm trying to find it here. It seems that the function lose tracks of some sort of functions. I would try to call this function in other places, just to see if the same happens. I saw this question, but i'm not sure if its the same yet (https://stackoverflow.com/questions/45861212/load-bingmaps-api-on-runtime) – Guilherme Nunes Mar 06 '19 at 17:10
  • I also get such issue when I try to load Bing Maps script in a place other than componentDidMount(). It will not recognize functions which are present in the index.html. Then it throws ShowMap() is not a function – Teja Swaroop Mar 06 '19 at 17:18
  • Understand. I can't reproduce here on my computer right now, but I see that you have to get the dom throught this line: var BingMaps = document.getElementById("BingMaps"); Is there another way to use this library? It can be that the DOM is not yet mounted when we call that function (when it says componentDidMount, then is when BingMaps start existing. – Guilherme Nunes Mar 06 '19 at 17:29
  • React use a Virtual Dom to avoid slow down the visualization and increase performance. For that reason, we almost dont use document.getElementById. There are a few alternatives, one include creating an instance utilizing in this way, for example let BingMap = window.nameOfTheJavascriptBingClass, and then working only in the Virtual Dom. Sorry, I didnt analyze your code properly, I'm just sharing a few informations that might be helpful. – Guilherme Nunes Mar 06 '19 at 17:29
  • Its throwing so many error, So I ended up in using React-BingMaps – Teja Swaroop Mar 07 '19 at 20:23
0
var Microsoft: any;

export class Home extends React.Component<RouteComponentProps<{}>, {}> {

constructor() {
    super();
}

loadBingMapScript() {
    var BingMaps = document.getElementById("BingMaps");
    if (BingMaps) {
        document.body.removeChild(BingMaps);
    }

    (window as any).loadMapScenario = () => this.InitMap();

    const script = document.createElement("script");
    script.src = "https://www.bing.com/maps/sdk/mapcontrol?callback=loadMapScenario";
    script.async = true;
    script.defer = true;

    script.id = "BingMaps";
    document.body.appendChild(script);
}

componentDidMount() {
    this.loadBingMapScript();
}

private InitMap(): void {
    Microsoft = (window as any).Microsoft;

    if (Microsoft !== undefined) {
        let mapElement: HTMLElement = this.refs.map as HTMLElement;

        var map = new Microsoft.Maps.Map(mapElement, {
            credentials: 'Credential',
            center: new Microsoft.Maps.Location(39.393486, -98.100769),
            zoom: 3
        });
        Microsoft.Maps.loadModule('Microsoft.Maps.Clustering', function () {
            // Creating sample Pushpin data within map view
            var pushpins = Microsoft.Maps.TestDataGenerator.getPushpins(1000, map.getBounds());
            var clusterLayer = new Microsoft.Maps.ClusterLayer(pushpins, { gridSize: 100 });
            map.layers.insert(clusterLayer);
        });
    }
}

public render() {
    return <div>
        <div style={{ height: "500px", width: "100%" }}>
            <div id="map" ref="map" ></div>
        </div>
    </div>
    }
}
Teja Swaroop
  • 185
  • 1
  • 12