1

I am trying to express my Openlayers application as a components based application. Having a single <Map /> component with childrens like <Marker />, I need to access my <Map /> component's this.map property from the <Marker />.

Take this markup from a representational component:

<Map center={[-1.81185, 52.44314]} zoom={6}>
    <Marker title="This is a marker" coordinate={[-1.81185, 52.44314]} />
</Map>

The <Map /> component:

export default class Map extends React.Component {

    static propTypes = {
        center: React.PropTypes.array.isRequired,
        zoom: React.PropTypes.number.isRequired
    }

    constructor(props) {
        super(props);
        this.map = null;
    }


    componentDidMount() {
        this.map = new ol.Map(/* code removed for brevity */);
    }

    renderChildren() {
        const { children } = this.props;

        if (!children) return;

        return React.Children.map(children, c => {
            return React.cloneElement(c, {
                map: this.map
            });
        })
    }

    render() {
        return <div id="map">{this.renderChildren()}</div>
    }

}

The <Marker /> component:

export default class Marker extends React.Component {

    static propTypes = {
        map: React.PropTypes.object,
        coordinate: React.PropTypes.array.isRequired,
        title: React.PropTypes.string
    }

    componentDidMount() {
        const { map, coordinate, title } = this.props;

        if (!map) return;

        var marker = createMarkerAndPlaceOn(map);
    }


    render() {
        return null;
    }
}

As you can see I tried passing the this.map property down, by cloning the element and give it the property.

However, because I need to rely on the DOM node #map to be rendered, I can first initialize my new ol.Map() in the <Map />'s componentDidMount() method. This means my child component does not get the instance of this.map when rendering.

Is there any clean, non anti-pattern, way of achieving this?

1 Answer 1

2

You can store map in the state and it'll be passed down to the children as soon as it's ready.

constructor(props) {
    super(props);
    this.state = {
        map: null
    }
    this.renderChildren = this.renderChildren.bind(this);
 }


componentDidMount() {
    this.setState({map : new ol.Map()});
}

renderChildren() {
    const { children } = this.props;

    if (!children) 
        return;

    if(!this.state.map)
        return <div>Loading markers</div>

    return React.Children.map(children, c => {
        return React.cloneElement(c, {
            map: this.state.map
        });
    })
}

jsfiddle

Sign up to request clarification or add additional context in comments.

1 Comment

This is exactly what I was looking for, thanks a lot! :-)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.