luzny luzny - 5 months ago 84
Javascript Question

How to pass component callback to children element in react-router?

I would like to wrap my current router by Layout with one MapBox and pass to children MapBox callback function:

<Route path='/posts' component={PostsList} />
<Route path="/posts/:id" component={Post} >
</Route>


Currently to handle callback I repeat MapBox on two components:

export default class MapBox extends React.Component {

mapMoved(e) {
const map = this.refs.map.leafletElement;
const coords = [map.getCenter().lat, map.getCenter().lng];
this.props.mapMoved(coords);
}
render () {
const { markers } = this.props;
return (
<Map id="map" ref='map' center={[53,17]} onMoveend={::this.mapMoved}>
<TileLayer
url='https://api.mapbox.com/v4/mapbox.streets/{z}/{x}/{y}.png?access_token=pk'
attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
/>

{markers}
</Map>
)
}
}

const mapStateToProps = (state) => ({
posts: state.posts,
currentPost: state.currentPost
});
export default connect(mapStateToProps)(Layout);

export default class Post extends React.Component {
mapMoved(coords) {
//set marker position via redux
}
render() {
const {post, posts} = this.props;
var marker = <Marker position={post.coords} draggable={true} />;
return (
<div>
{post.title}

<MapBox mapMoved={::this.mapMoved} markers={marker} />
</div>
);
}
}
const mapStateToProps = (state) => ({
post: state.post,
posts: state.posts,
});
export default connect(mapStateToProps)(Post);

export default class PostsList extends React.Component {
renderList() {
const {posts} = this.props;
}
mapMoved(coords) {
// redux fetch nearest posts
}
render() {
const markers = this.props.markers.map(p => <Marker position={p.coords} />);
return (
<div>
{::this.renderList()}
<MapBox mapMoved={::this.mapMoved} markers={markers} />
</div>
);
}
}
const mapStateToProps = (state) => ({
posts: state.posts,
});
export default connect(mapStateToProps)(PostsList);


Desired routes wrapped by Layout:

<Route component={Layout}>
<Route path='/posts' component={PostsList} />
<Route path="/posts/:id" component={Post} >
</Route>
</Route>


Desired Layout component:

export default class Layout extends React.Component {
render() {
return (
<div>
{this.props.children} //I would like to pass mapMoved callback here
<MapBox mapMoved={::this.mapMoved} markers={this.props.markers} />
</div>
);
}
}


I'm using Redux, maybe should I pass somehow callback function trough it?

Answer

You can pass props to children like so

export default class Layout extends React.Component {
  mapMoved(map) {}
  render() {
    return (
      <div>
        {React.cloneElement(this.props.children || <div />, {mapMoved: (map) => this.mapMoved(map)})} //I would like to pass mapMoved callback here
        <MapBox mapMoved={::this.mapMoved} posts={this.props.posts} />
      </div>
    );
  }
}

const mapStateToProps = (state) => ({
  posts: state.posts,
});

export default connect(mapStateToProps)(Layout);