Bug Description
AnvilEditorCanvas registers a click handler via �ddEventListener in a useEffect with an empty dependency array ([]). The handler closes over drag/resize state values from Redux. Because the effect never re-runs, the handler always uses the state captured at mount time. When drag state changes, the click guard reads stale values and fails to suppress canvas clicks during active drag operations.
Affected files
�pp/client/src/layoutSystems/anvil/editor/canvas/AnvilEditorCanvas.tsx, lines 47-53:
ypescript useEffect(() => { canvasRef.current?.addEventListener("click", handleOnClickCapture); return () => { canvasRef.current?.removeEventListener("click", handleOnClickCapture); }; }, []); // empty deps - handler never updated
�pp/client/src/layoutSystems/anvil/editor/canvas/hooks/useClickToClearSelections.ts, lines 18-31:
` ypescript
const isDragging = useSelector(state => state.ui.widgetDragResize.isDragging);
const isCanvasResizing = useSelector(state => state.ui.widgetDragResize.isAutoCanvasResizing);
const isDistributingSpace = useSelector(getAnvilSpaceDistributionStatus);
return (e) => {
if (!(isDragging || isCanvasResizing || isDistributingSpace)) { // stale values
goToWidgetAdd();
focusWidget(widgetId);
showPropertyPane();
e.preventDefault();
}
};
`
Failure scenario
- User opens the Anvil editor. At mount, isDragging = false.
- User starts dragging a widget. Redux updates isDragging = true.
- useClickToClearSelections returns a new function with the updated value.
- useCallback creates a new handleOnClickCapture reference.
- The useEffect with [] deps does NOT re-run - the old handler (with isDragging = false) remains registered.
- User clicks the canvas during the drag.
- The stale handler sees isDragging = false, passes the guard, and calls goToWidgetAdd(), ocusWidget(), and showPropertyPane().
- Widget selection is disrupted mid-drag. The property pane jumps to a different widget. The drag may be cancelled unexpectedly.
Fix
Add handleOnClickCapture to the dependency array, or use a ref to hold the latest handler:
` ypescript
const handleOnClickCaptureRef = useRef(handleOnClickCapture);
useEffect(() => { handleOnClickCaptureRef.current = handleOnClickCapture; });
useEffect(() => {
const handler = (e: MouseEvent) => handleOnClickCaptureRef.current(e);
canvasRef.current?.addEventListener("click", handler);
return () => { canvasRef.current?.removeEventListener("click", handler); };
}, []);
`
Environment
Appsmith elease branch (2026-08-13), React 18.
Bug Description
AnvilEditorCanvas registers a click handler via �ddEventListener in a useEffect with an empty dependency array ([]). The handler closes over drag/resize state values from Redux. Because the effect never re-runs, the handler always uses the state captured at mount time. When drag state changes, the click guard reads stale values and fails to suppress canvas clicks during active drag operations.
Affected files
�pp/client/src/layoutSystems/anvil/editor/canvas/AnvilEditorCanvas.tsx, lines 47-53:
ypescript useEffect(() => { canvasRef.current?.addEventListener("click", handleOnClickCapture); return () => { canvasRef.current?.removeEventListener("click", handleOnClickCapture); }; }, []); // empty deps - handler never updated�pp/client/src/layoutSystems/anvil/editor/canvas/hooks/useClickToClearSelections.ts, lines 18-31:
` ypescript
const isDragging = useSelector(state => state.ui.widgetDragResize.isDragging);
const isCanvasResizing = useSelector(state => state.ui.widgetDragResize.isAutoCanvasResizing);
const isDistributingSpace = useSelector(getAnvilSpaceDistributionStatus);
return (e) => {
if (!(isDragging || isCanvasResizing || isDistributingSpace)) { // stale values
goToWidgetAdd();
focusWidget(widgetId);
showPropertyPane();
e.preventDefault();
}
};
`
Failure scenario
Fix
Add handleOnClickCapture to the dependency array, or use a ref to hold the latest handler:
` ypescript
const handleOnClickCaptureRef = useRef(handleOnClickCapture);
useEffect(() => { handleOnClickCaptureRef.current = handleOnClickCapture; });
useEffect(() => {
const handler = (e: MouseEvent) => handleOnClickCaptureRef.current(e);
canvasRef.current?.addEventListener("click", handler);
return () => { canvasRef.current?.removeEventListener("click", handler); };
}, []);
`
Environment
Appsmith elease branch (2026-08-13), React 18.