Target:

So I want to be able to let the user see the other end of the travel when they click “Go to the departure (destination) point”. This means I need to have reference to the Marker of the other end of the travel.

My first failed naive attempt:

1
2
3
4
5
6
7
8
9
10
11
travel.fromMarker = <Marker
position={travel.from}
icon={generateTravelIcon(travel.travel_rgb, travel.mode, true)}
>
<Popup>
...
<a href='#' onClick={()=>{travel.toMarker.click()}}>
Go to destination point
</a>
</Popup>
</Marker>

This did not even feel right!

Keep in mind, however, that the JSX doesn’t return a component instance! It’s just a ReactElement: a lightweight representation that tells React what the mounted component should look like.
– From Refs to Components

React createRef() and ref

What I need is refs:

Refs provide a way to access DOM nodes or React elements created in the render method.
– From Refs and the DOM

Here’s what I did to make the pair of Markers:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class TravelMarkerPair extends React.Component {
static propTypes = {
travel: PropTypes.object.isRequired,
};

constructor(props) {
super(props);
this.fromMarker = React.createRef();
this.toMarker = React.createRef();
}

toggleFocus(clickTo) {
clickTo
? this.fromMarker.current.fireLeafletEvent('click')
: this.toMarker.current.fireLeafletEvent('click');
}

render() {
const travel = this.props.travel;
return (
<>
<Marker
position={travel.to}
icon={generateTravelIcon(travel.travel_rgb, travel.mode, true)}
ref={this.toMarker}
>
<Popup>
...
<a href='#' onClick={() => this.toggleFocus(true)}>
Go to departure point
</a>
</Popup>
</Marker>
<Marker
position={travel.from}
icon={generateTravelIcon(travel.travel_rgb, travel.mode, true)}
ref={this.fromMarker}
>
<Popup>
...
<a href='#' onClick={() => this.toggleFocus(false)}>
Go to destination point
</a>
</Popup>
</Marker>
</>
);
}
}

So I am able to refer to the component with this.toMarker and this.fromMarker, and then I can fire the leaflet event of clicking them by this.toMarker.current.fireLeafletEvent('click') when user clicks the link from its sibling.

Finding out the methods available to the component

I did not find the method fireLeafletEvent very easily. I tried click() but was told that this.toMarker.current.click is not a function. I then thought if it was because React component did not have click(), and tried this provided in a stackoverflow post:

1
2
3
4
5
6
7
8
9
10
11
12
13
const mouseClickEvents = ['mousedown', 'mouseup'];
function simulateMouseClick(element){
mouseClickEvents.forEach(mouseEventType =>
element.dispatchEvent(
new MouseEvent(mouseEventType, {
view: window,
bubbles: true,
cancelable: true,
buttons: 1
})
)
);
}

I was told that this.toMarker.current.dispatchEvent was not a function either.

Yihao helped me with logging all the properties available of a JavaScript object.

This will print ALL the properties, including inherited ones and own ones:

1
2
3
for (const p in component) {
console.log(p)
}

This will not print inherited properties:

1
console.log(Object.getOwnPropertyNames(component));

This will only print functions:
typeof object[property] == ‘function’

1
2
3
4
for (const p in component) {
if (typeof component[p] === "function")
console.log(p)
}

This is how I found there is a fireLeafletEvent method that suits my need.