Reputation: 2025
I know there's been many questions with this topic asked already, but it really feels like each and every one of them is different and I cannot find one that matches my issue closely enough.
I have a grid with draggable ItemComponent
s. Once selected, additional action icons show up (ItemActionIcon
). I would very much like to unselect the component (and effectively hide the action icons) once one of the actions is clicked.
and so in line 77 <div onClick={() => this.setState({selected: false})} key={index}>
I'm attempting to update the state of selected
to false. It already works just fine in all other cases mentioned in the file. But not in this case. When I click the icon, I can see with a debugger (or with a console.log when I tried it) that the onClick
action is triggered as expected and the ItemComponent
even gets another call to the render
method, but the this.state.selected
is still set to true
.
import React, {Component} from "react";
import Draggable, {DraggableBounds} from "react-draggable";
import ItemComponentAction from "./ItemComponentAction";
import ItemActionIcon from "./ItemActionIcon";
export interface Position {
x: number;
y: number;
}
export interface ItemComponentProps {
gridSquareSize: number;
canvasBounds: DraggableBounds;
margin: number;
position: Position;
}
interface ItemComponentState {
gridSquareSize: number;
canvasBounds: DraggableBounds;
margin: number;
selected: boolean;
}
export default abstract class ItemComponent extends Component<ItemComponentProps> {
protected abstract readonly icon: string;
protected abstract readonly actions: ItemComponentAction[];
state: ItemComponentState;
protected constructor(props: ItemComponentProps) {
super(props);
this.state = {
gridSquareSize: props.gridSquareSize,
canvasBounds: props.canvasBounds,
margin: props.margin,
selected: false
};
}
render() {
return (
<Draggable grid={[this.state.gridSquareSize / 2, this.state.gridSquareSize / 2]}
defaultPosition={{
x: this.state.margin + this.props.position.x * this.state.gridSquareSize,
y: this.state.margin + this.props.position.y * this.state.gridSquareSize
}}
handle=".handle"
bounds={this.state.canvasBounds}
onDrag={() => this.setState({selected: false})}
>
<div tabIndex={0}
className="handle"
onClick={() => this.setState({selected: true})}
onBlur={() => this.setState({selected: false})}
style={{
position: 'absolute',
backgroundColor: 'red',
width: this.state.gridSquareSize,
height: this.state.gridSquareSize,
cursor: "move"
}}
>
{this.icon}
{
!this.state.selected || !this.actions.length
? null
: (
<div style={{
position: 'absolute',
bottom: "0"
}}>
{
this.actions.map((action, index) => (
<div onClick={() => this.setState({selected: false})} key={index}>
<ItemActionIcon {...action}/>
</div>
))
}
</div>
)
}
</div>
</Draggable>
);
}
}
so what's the deal?
Upvotes: 0
Views: 195
Reputation: 11591
The outer <div>
of your component has its own onClick
handler which is setting the value of your state back to false
. Try using stopPropagation()
on the inner onClick
handled event. That will prevent the event from propagating to the outer parent <div>
, and only the inner onClick
handler will execute when it is clicked on.
{
!this.state.selected || !this.actions.length ? null : (
<div
style={{
position: "absolute",
bottom: "0"
}}
>
{this.actions.map((action, index) => (
<div
onClick={e => {
e.stopPropagation();
this.setState({ selected: false });
}}
key={index}
>
<ItemActionIcon {...action} />
</div>
))}
</div>
);
}
Upvotes: 1