The Problem with react-baidu-maps and the Abuse of PropTypes

How using cloneElement and magic propTypes can prevent a component from being really composable.

Clara Dopico
Trabe
4 min readMay 4, 2020

--

Photo by Hanson Lu on Unsplash

In my last post, we learned how to render some Baidu Maps in a simple way to avoid the Google Maps block in China. We reviewed and tried react-baidu-maps, a React library to render Baidu Maps. In those examples, we used the components exported by the library, like BaiduMap or Marker.

One of our projects is a React based framework that provides different components, including maps. Instead of exposing the react-baidu-maps components, we opted to wrap them and provide an abstraction to the user. This way we encapsulate the library internals controlling which props we want to expose to the user. We can even set some defaultProps to ensure a map will always be rendered even if some props are missing.

Our component library

Our custom Baidu Map implementation is defined in the following snippet. We wanted to control which props were available and their default values:

Our Baidu Map implementation

As it is defined in the PropTypes, only the Baidu Maps API Key prop is required. A line like <BaiduMap apiKey="MY_API_KEY" /> will be enough to render a Baidu Map.

A Baidu Map using our default props

Once we achieved this we were drawn to do the same with the other components we wanted to expose. For example, our own marker component:

Our Baidu Marker implementation

We exposed some props and set some default values as well. But when we wanted to try out our map… Oh no! The map was not rendering and we found some errors on the console.

Chrome DevTools console error

The problem with react-baidu-maps

We faced the same problem when we tried to wrap other react-baidu-maps components like InfoWindow or MarkerClusterer. As it is shown in the console, the error is thrown in the library Marker component code. So the logical next move was to review the file to understand what that Cannot read property 'addOverlay' of undefined meant.

Debugging the code we found that the error was thrown in the this.props[MAP].addOverlay(this.marker); line in thecomponentDidMount method:

react-baidu-maps Marker component

As we can see in the snippet above, Marker has two propTypes objects [MAP] and [MARKER_CLUSTERER], two named constants imported from “utils/constants.js”:

react-baidu-maps constants.js file

So…the secret is out. Some overlay components like InfoWindow or Marker must have props with these funny names. Searching for the uses of these constants in the library, we found the BaiduMap component:

react-baidu-maps BaiduMap

The renderChildren method checks if each of the Map children contains a [MAP] propType (also known as SECRET_MAP_DO_NOT_USE_OR_YOU_WILL_BE_FIRED). In that case, it injects a map element in the mentioned SECRET_MAP_DO_NOT_USE_OR_YOU_WILL_BE_FIRED prop. This checking is necessary to ensure that a compatible element, like a Marker or InfoWindow, is being cloned with React cloneElement. Here is where our problem originated: our custom components did not have a propType with that name.

More react-baidu-maps Marker component

In the Marker case, if no prop called SECRET_MAP_DO_NOT_USE_OR_YOU_WILL_BE_FIRED is received that prop will be undefined. Thus, the previously mentioned line this.props[MAP].addOverlay(this.marker) in the componentDidMount method will throw an error.

A solution

In an ideal world, the react-baidu-maps would use the React Context API instead of using cloneElement and filtering the children using magic propTypes. Sadly, it is not the case, so we have to play by the library rules and adapt our code to this.

We need to add the required propTypes to our custom components. We can create our own “utils/constants.js” to hide the funny naming, or directly add the constants like in the following examples:

Our Baidu Marker implementation with the necessary propTypes

As we can see in the react-baidu-maps InfoWindow source code, this overlay component expects two props [MAP] and [MARKER], so we need to add the corresponding two propTypes:

Our Info Window implementation with the necessary propTypes

Once we export our custom components, we can simply build an example like the following one:

This way we can successfully render a Baidu Map without any errors:

A Baidu Map with a Marker and an InfoWindow

Wrapping up

react-baidu-maps is a library designed to be used through its exported components. In some cases, you might need to override its behavior or you want to hide the library internals from the user. Due to the library internals, based on filtering the children using magic propTypes and cloning elements, you will need to add some specifics propTypes to your custom components. This is a messy approach but it will avoid some errors, headaches and you will get your Baidu Maps running. There are other APIs, like the Context API, that let components pass data to deeply nested descendants, that will adapt better to this scenario and are composable-friendly solutions.

--

--