0
votes

I have here some interesting case! I'm using a react-input-range inside a react-leaflet Marker Popup

Which is something like this:

import React from 'react';
import { Map as LeafletMap, Marker, Popup, TileLayer } from 'react-leaflet';
import InputRange from 'react-input-range';

export default class MapPage extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: { min: 2, max: 200 },
      name: 'sample name',
    };
  }
  render() {
    return (
      <div>
        {/*
          * This input range outside LeafletMap works
        */}
        <InputRange
          maxValue={300}
          minValue={0}
          value={this.state.value}
          onChange={value => this.setState({ value })}
        />
        <LeafletMap center={[0, 0]} zoom={16}>
          <TileLayer
            attribution="&copy; <a href=&quot;http://osm.org/copyright&quot;>OpenStreetMap</a> contributors"
            url="http://{s}.tile.osm.org/{z}/{x}/{y}.png"
          />
          <Marker position={[0, 0]}>
            <Popup>
              {/*
                * This input below works
              */}
              <input
                value={this.state.name}
                onChange={({ target }) => this.setState({ name: target.value })}
              />
              {/*
                * This input range INSIDE LeafletMap does not work
              */}
              <InputRange
                maxValue={300}
                minValue={0}
                value={this.state.value}
                onChange={value => this.setState({ value })}
              />
            </Popup>
          </Marker>
        </LeafletMap>
      </div>
    );
  }
}

What I know so far

  • The input range component works outside the LeafletMap component.
  • Native input works inside the LeafletMap component!
  • I've confirmed that a native input works inside the popup! The input range works on mobile(android chrome browser)! (swipe & click events)

Any ideas how to make input range work inside react-leaflet map?

Here's a few more details that might help:

  • react-leaflet: ^2.0.0-rc.3
  • react: 16.4.2
  • react-input-range: ^1.3.0

Testing Env

Browsers: Chrome 64.0.32, Firefox 61 and Safari 11.1

OS: Mac OSX High Sierra 10.13.5

Codepen: https://codepen.io/codepen-theo/pen/OwdLXY?editors=0010

1
Are there any console errors? Also, have you tried debugging the props with this tool at all? chrome.google.com/webstore/detail/react-developer-tools/… - dkniffin
no errors mate, and events weren't triggered at all. I am using github.com/react-boilerplate/react-boilerplate w/ github.com/evgenyrodionov/redux-logger since the react-dev tools did not play well with the boilerplate @dkniffin - Theo
Does it work with multiple InputRange at all? This library seems to have do some very confusing stuff to attach and destroy event listeners on the document node. I suspect that instances of the components might be messing with the global event listeners from the other InputRange components. - Håken Lid
Hi @HåkenLid, i tried adding 2 input ranges at the same time(outside map), both worked. does this clear some of your assumptions? - Theo
Yes. But I suspect that the problem might be related to how react-input-range handles events. It needs to attach event listeners to the document node, but with leaflet, there's some complicated stuff going on using react portals and iframes. So my guess is that some assumptions made by input range are not true in this case. Can you link to a running example of your code in a codepen or something like that? The specific combination of these two libraries is probabaly not so common. - Håken Lid

1 Answers

2
votes

I have figured out what's happening. But I'm not sure there's a good way to get it to work as you want.

What happens is that leaflet.js will call event.stopPropagation() on the mousedown event when it bubbles to the popup element. This blocks reacts synthetic events from working as expected. React's event propagation is handled independently of the native bubbling / capturing.

https://github.com/Leaflet/Leaflet/blob/master/src/layer/Popup.js#L185

You can monkey patch the the event listener on the popup wrapper. See this example:

https://codepen.io/haakenlid/pen/ejxNKE

This works by simply removing one of the preventDefault event listener that leaflet.js has attached to the plugin wrapper node. (In a very hacky way)

class MyPopup extends React.Component {
  componentDidMount(){
    if (!this.popup) return
    const el = this.popup.leafletElement
    el._initLayout()
    const wrapper = el._wrapper
    wrapper.removeEventListener('mousedown', wrapper._leaflet_events['mousedown6'])
    console.log('mount')
  }
  render(){
    return <Popup ref={ el=>this.popup = el } {...this.props} />
  }
}

This is just a proof of concept. There's a high likelihood that it will break again with future versions of leaflet.js (or in some other way).