Interactive Maps Where You Can Pick a Style or Theme with React
Tags: javascript, react, maps
Published: 2021-06-18 18:43:24

---
title: Interactive Maps Where You Can Pick a Style or Theme with React
published: true
description: Maps can have a variety of looks and feels which may be a personal preference
tags: javascript,react,maps
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/c825serbu4mmu56k9mgi.png
canonical_url: https://developer.here.com/blog/use-here-interactive-maps-with-reactjs-to-pick-a-theme
---

For this project, I want to display an interactive map that allows the user to choose a theme. A slippy map like this that allows the user to pan and zoom around is one of the most common maps on the web. Since it may not be straightforward how to fetch raster tiles and build the standard behaviors into a UI, using the Maps JavaScript SDK  is invaluable for a consistent experience.

By clicking one of the thumbnail images, the interactive map will update with a new tile service provider as demonstrated here:

![theme picker demo](https://cdn2.hubspot.net/hubfs/2174253/theme-picker-demo.gif)

# Basic React

For a basic single-page app, you might start by including the React and HERE libraries from a CDN directly in your index.html.

```javascript
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
```

Create a simple ES6 class called `SimpleHereMap`. The `componentDidMount()` method runs after the `render()` method per the [React Component Lifecycle](https://reactjs.org/docs/state-and-lifecycle.html) which means we can more or less include the HERE JavaScript [Quick Start](https://developer.here.com/documentation/maps/topics/quick-start.html) code just as is.

```javascript
 
const e = React.createElement;

class SimpleHereMap extends React.Component {
  componentDidMount() {
    var platform = new H.service.Platform({
        app_id: 'APP_ID_HERE',
        app_code: 'APP_CODE_HERE',
        })

    var layers = platform.createDefaultLayers();
    var map = new H.Map(
        document.getElementById('map'),
        layers.normal.map,
        {
            center: {lat: 42.345978, lng: -83.0405},
            zoom: 12,
        });

    var events = new H.mapevents.MapEvents(map);
    var behavior = new H.mapevents.Behavior(events);
    var ui = H.ui.UI.createDefault(map, layers);
  }

  render() {
      return e('div', {"id": "map"});
  }
}

const domContainer = document.querySelector('#app');
ReactDOM.render(e(SimpleHereMap), domContainer);
```

This example works if you use it standalone in a single index.html file but doesn’t make use of JSX and falls apart if you try to use `create-react-app`. If you use that tool as described in a few of the other ReactJS posts you may see the next error.

# ‘H’ is not defined no-undef

Adapting the above example for `create-react-app` requires a few minor changes.

1. Move the includes of the HERE script libraries into public/index.html
2. Create a Map.js with the SimpleHereMap class.
3. Update the `render()` method to use JSX to place the <div/> element.

If you make those changes and `npm start` you will likely see the following error in your console:

*‘H’ is not defined no-undef*

The initialization of `H.service.Platform()` is causing an error because **H** is not in scope. This is not unique to HERE and is generally the case with any 3rd party code you try to include with React. Using `create-react-app` implies using its toolchain including [webpack](https://webpack.js.org/) as a module bundler, [eslint](https://eslint.org/) for checking syntax, and [Babel](https://babeljs.io/) to transpile JSX.

Any library like the HERE JavaScript SDK that has a global variable like **H** might run into a similar problem during compilation (jQuery, Leaflet, etc.). By referencing non-imported code like this, the syntax linter which is platform agnostic will complain because it doesn’t know that the page will ultimately be rendered in a web browser.

The simple fix is to reference `window.H` instead. Unfortunately, this does violate one of the basic principles of building modular JavaScript applications by tightly coupling our `<script>` includes with our component but it works.

# public/index.html

The script libraries are simply included in the public index.html.

```javascript
 
@@ -4,6 +4,14 @@
     <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+
+    <link rel="stylesheet" type="text/css" href="https://js.api.here.com/v3/3.0/mapsjs-ui.css?dp-version=1526040296" />
+
+    <script type="text/javascript" src="https://js.api.here.com/v3/3.0/mapsjs-core.js"></script>
+    <script type="text/javascript" src="https://js.api.here.com/v3/3.0/mapsjs-service.js"></script>
+    <script type="text/javascript" src="https://js.api.here.com/v3/3.0/mapsjs-ui.js"></script>
+    <script type="text/javascript" src="https://js.api.here.com/v3/3.0/mapsjs-mapevents.js"></script>
+
```

# src/Map.js

The **Map** component defines the rendered map. We’ll be making a few more changes to this class later once we get to the theme selection.  We're storing a lot of the properties like lat, long, zoom, and app credentials as state so that they can be changed dynamically.

```javascript
 
class Map extends Component {
    constructor(props) {
        super(props);

        this.platform = null;
        this.map = null;

        this.state = {
            app_id: props.app_id,
            app_code: props.app_code,
            center: {
                lat: props.lat,
                lng: props.lng,
            },
            zoom: props.zoom,
            theme: props.theme,
            style: props.style,
        }
    }

    // TODO: Add theme selection discussed later HERE

    componentDidMount() {
        this.platform = new window.H.service.Platform(this.state);

        var layer = this.platform.createDefaultLayers();
        var container = document.getElementById('here-map');

        this.map = new window.H.Map(container, layer.normal.map, {
            center: this.state.center,
            zoom: this.state.zoom,
          })

        var events = new window.H.mapevents.MapEvents(this.map);
        // eslint-disable-next-line
        var behavior = new window.H.mapevents.Behavior(events);
        // eslint-disable-next-line
        var ui = new window.H.ui.UI.createDefault(this.map, layer)
    }    

    render() {
        return (
            <div id="here-map" style={{width: '100%', height: '400px', background: 'grey' }} />
        );
    }
}

```

At this point though we have a working and extensible ReactJS component that is ready to display a HERE Interactive Maps.

# Themes

Since a map can be an extension to a brand or preferences, there are many themes and styles available for how to present a map on a page. The following image depicts some of the examples of maps you can use from the Maps Tile API.

![Sample Themes](https://dev-to-uploads.s3.amazonaws.com/i/c825serbu4mmu56k9mgi.png)

The **src/ThemeSelector.js** component is simply intended to provide a listing of thumbnail images the user can choose from. It includes some of the more popular themes:

```javascript
 
class ThemeSelector extends Component {
    render() {
        var themes = [
            'normal.day',
            'normal.day.grey',
            'normal.day.transit',
            'normal.night',
            'normal.night.grey',
            'reduced.night',
            'reduced.day',
            'pedestrian.day',
            'pedestrian.night',
        ];

        var thumbnails = [];
        var onChange = this.props.changeTheme;
        themes.forEach(function(theme) {
            thumbnails.push(<img key={ theme } src={ 'images/' + theme + '.thumb.png' } onClick= { onChange } alt={ theme } id={ theme } />);
        });

        return (
            <div>
            { thumbnails }
            </div>
        );

    }
}
```

To make the click event work, we’re going to add a bit more to our src/Map.js component. The `changeTheme` method described below is an example like you’d find for most any HERE JavaScript implementation.

```javascript
 
changeTheme(theme, style) {
    var tiles = this.platform.getMapTileService({'type': 'base'});
    var layer = tiles.createTileLayer(
        'maptile',
        theme,
        256,
        'png',
        {'style': style}
    );
    this.map.setBaseLayer(layer);
}
```

We will call this method from the `shouldComponentUpdate()` method. From the React Component Lifecycle, this method is called when state changes occur in order to determine if it’s necessary to re-render the component. When we select a new theme, we call the `setBaseLayer` method and can update the map without requiring React to make a more costly re-render of the entire DOM.

```javascript
 
shouldComponentUpdate(props, state) {
    this.changeTheme(props.theme, props.style);
    return false;
}
```

# App

Putting it all together, we use **src/App.js** to track state for the theme selection as the common ancestor to both the **Map** and **ThemeSelector** components.

The source code looks like this:

```javascript
 
import Map from './Map.js';
import ThemeSelector from './ThemeSelector.js';

class App extends Component {
    constructor(props) {
        super(props);

        this.state = {
            theme: 'normal.day',
        }

        this.onChange = this.onChange.bind(this);
    }

    onChange(evt) {
        evt.preventDefault();

        var change = evt.target.id;
        this.setState({
            "theme": change,
        });
    }

    render() {
        return (
            <div className="App">
                <SimpleHereMap
                    app_id="APP_ID_HERE"
                    app_code="APP_CODE_HERE"
                    lat="42.345978"
                    lng="-83.0405"
                    zoom="12"
                    theme={ this.state.theme }
                />
                <ThemeSelector changeTheme={ this.onChange } />
            </div>
        );
    }
}
```

# Summary

Ideally we’d like to include an `npm` package that encapsulates the HERE Map APIs as React components for use in our applications. There are some community projects to create these packages but your experience may vary depending on which one you choose to use. It would be good to know what you’ve used successfully, so leave a note in the comments.

For everybody else just looking for a quick way to get compatibility with many of the other JavaScript API examples hopefully the window.H trick is what you were looking for.

You can find the [source code](https://github.com/j12y/HERE-react-map-theme-picker) for this project on GitHub.