React Router Link not working with LeafletJS
Versions:
- react-router-dom 4.1.1
- react-router-redux 5.0.0-alpha.4
- reactive sheet 1.1.3
- sheet 1.0.3
Steps to reproduce
I am creating a brochure map. In which I add some markers. These markers have pop-ups. In each of these popups I want to have<Link>
Also if it helps, this is my routing config:
ReactDOM.render(
<Provider store={store}>
<div>
<AppContainer />
<ConnectedRouter history={history}>
<div>
<MenuContainer />
<Switch>
<Route path='/:area/:sport/list' component={ListContainer} />
<Route path='/:area/:sport/map' component={MapContainer} />
<Route path='/:area/:sport/rasp' component={RaspContainer} />
<Route path='/:shortcode/details' component={StationDetailsContainer} />
<Redirect exact from='/' to='/wellington/paragliding/list' />
<Route component={NoMatch} />
</Switch>
</div>
</ConnectedRouter>
</div>
</Provider>,
document.getElementById('root')
)
Expected behavior
I see my link and click on it when the popup opens.
Actual behavior
The link could not be seen. It is not generated.
additional information
On the inside, <MapMode>
I use <Map>
the flyer. If I set <Link>
just above the tag <Map>
it works. As soon as I want to have a link inside mine <Map>
, somehow it breaks. This is the React structure of my page, the tag <Popup>
just contains null
when the Javascript breaks:
This is a pretty tricky issue, so feel free to ask me questions. Thank.
source to share
I'm not 100% sure about this answer. But I'm going to try it anyway, because I think that at least it can shed some light on anyone who tries to fix this problem in the future.
I got my first tip from this issue in the react-leaflet
GitHub repo. Consistent with this and your error, it seems the problem is that the Popup cannot be accessed router
from context
because it is context
not passed to the Popup with how they display it. So we have to fix the problem if we can explicitly pass the Popup context.
Then I found a way to explicitly pass the context to the component in this StackOverflow answer. With that, I think you should be able to use HoC (higher order component) in the following way to solve your problem.
This is a HoC that injects context into a component:
function withContext(WrappedComponent, context){
class ContextProvider extends React.Component {
getChildContext() {
return context;
}
render() {
return <WrappedComponent {...this.props} />
}
}
ContextProvider.childContextTypes = {};
Object.keys(context).forEach(key => {
ContextProvider.childContextTypes[key] = React.PropTypes.any.isRequired;
});
return ContextProvider;
}
Let's say you are using a Popup inside a MapMaker component. Then you can insert the context using router
into the Popup using HoC like this.
class MapMaker extends React.Component {
//......
// This make sure you have router in you this.context
// Also you can add any other context that you need to pass into Popup
static contextTypes = {
router: React.PropTypes.object.isRequired
}
render(){
const PopupWithContext = withContext(Popup, this.context);
return (
//..... your JSX before Popup
<PopupWithContext/> // with your props
//..... your JSX after Popup
);
}
}
source to share
I tried the solution Tharaka suggested, but it didn't work for me. It looks like the "tooltip" is using its own context, thereby blocking the context that is passed from higher levels. However, inspired by this solution, I came up with another one, very simple and based on the principle of composition.
I have created a RouterForwarder component
import React, { Component } from 'react'
import PropTypes from 'prop-types'
class RouterForwarder extends Component {
getChildContext() {
return this.props.context
}
render() {
return <span>{this.props.children}</span>
}
}
RouterForwarder.childContextTypes = {
router: PropTypes.object.isRequired,
}
RouterForwarder.propTypes = {
context: PropTypes.object.isRequired,
}
export default RouterForwarder
and then used it in my component (the one that displays Map, Marker, Popup and Link) like this:
import RouterForwarder from './RouterForwarder'
class MyComponent extends Component {
render() {
return (
...
<Popup>
<RouterForwarder context={this.context}>
<Link to={'my destination'}>Go to My Destination</Link>
</RouterForwarder>
</Popup>
...)
}
}
MyComponent.contextTypes = {
router: PropTypes.object,
}
source to share
My solution (this is a workaround, but works great and I don't see any drawbacks):
I decided to use (in the reply router v3 with a shortcut )
<a onClick={() => goTo(params)} />
whereas goTo
defined in
const mapDispatchToProps = (dispatch) => bindActionCreators({
goTo(id) {
return push(`/<url>/${id}`); // push from 'react-router-redux'
},
}, dispatch);
source to share