Hi,
Thanks very much for addressing my question. I'm trying to programatically highlight features based on user input and it looks like, from reading the documentation, and the related blog post, I can set the highlightedObjectIndex layer property to the specific index of the instanced object. However, it's not clear from the documentation how to actually do this properly.
I'd like to highlight specific features from a list of picked features. Currently, I have this handler method:
handleHighlight(feature) {
feature.layer.props.highlightedObjectIndex = feature.index
}
Where "feature" is a single, picked feature.
This gives me a nasty error:
Uncaught TypeError: Cannot assign to read only property 'highlightedObjectIndex' of object '[object Object]'
If I manually hard-code an index in the layer props, for example highlightedObjectIndex: 97, the feature with that index is then highlighted, like I expected.
What is the proper way to programatically set this property? Thanks again.
Best,
Todd
What is the proper way to programatically set this property?
deck.gl API is optimized for a functional programming style, so layers are immutable.
You need to create a new Layer instance with the the same id as the old one and add all props included the updated prop and then call deck.setProps({layers: newLayer, ... (old layers)]}).
Thank you for the quick reply. I think I'm making progress... here is my handler method now:
handleHighlight(feature) {
const layer = new GeoJsonLayer({
...feature.layer.props,
highlightedObjectIndex: feature.index
})
this.props.deckRef.deck.setProps({layers: layer})
}
Where feature is a feature from a list of picked features, using deck.pickObjects.
I feel like this isn't the proper, React-like method of accessing the deck object, but I added a ref deckRef to the Redux store so I could access it across components that are children of different parents. This seems to update the highlightedObjectIndex property on the deck object.
For example, console.log(this.props.deckRef.deck.props.layers.props.highlightedObjectIndex) prints out the new value of highlightedObjectIndex.
However, every time my handler method is called all the rendered features disappear, and reappear when I interact with the map (i.e., zoom and pan). I feel like I'm still missing something. Am I inadvertently sidestepping the draw cycle by using a React ref, or do I need to tell Deck.gl to redraw my new layer explicitly? Thanks again.
Best,
Todd
If you are using react, you should just create new layers in your JSX code when you rerender as a result of state changes.
I.e. handleHighlight should update application state (using e.g. setState or redux), which will trigger a react re-render. Then you just set the highlightedObjectIndex prop to the new value when you create new layers to pass to <DeckGL> react component.
You may want to spend some time reviewing the examples to get a better sense for how to manage state changes in React.
Thank you for your suggestion. I took a step back and revisited some basic state management techniques in React and solved my issue. I was making it much more complicated than necessary... Again, thanks for helping make Deck.gl such a wonderful library to work with!
Here's my solution for anyone like me who stumbles upon this thread:
My component structure is like so:
Root, App component:
Changes to any props for my DeckGL layer component are passed to the App component state through a callback passed to my UI component (see bellow). This uses the technique for lifting state illustrated very well in the React docs.
class App extends React.Component {
constructor(props){
super(props);
this.state = {
layerProps: {}
}
}
setLayerProps = (layerProps = {}) => {
this.setState({
layerProps: {...layerProps}
})
}
render () {
return (
<UiComponent setLayerProps={this.setLayerProps}>
<Map layerProps={this.state.layerProps} setLayerProps={this.setLayerProps}/>
</UiComponent>
)
}
}
export default App;
My UiComponent component:
class UiComponent extends Component {
handleHighlight(feature) {
this.props.setLayerProps({
highlightedObjectIndex: feature.index
})
}
renderList() {
return this.props.features.map(feature => {
return (
<List.Item
key={feature.index}
onClick={() => this.handleHighlight(feature)}
>
<List.Content>
<List.Header>
{feature.object.properties.name}
</List.Header>
</List.Content>
</List.Item>
)
})
}
render(){
return(
<List>
{this.renderList()}
</List>
)
}
}
I then just pass down the additional parameters, stored in the App state, down to my DeckGL GeoJsonLayer (a child of the Map component) using a simple spread operator:
const layer = new GeoJsonLayer({
id: 'articles-layer',
// other default parameters...
...this.props.layerProps
})