Hello everyone,
I am working with ArcGIS API for JavaScript 4.34 in a React + Vite (TypeScript) project.
I was able without any issue to:
initialize a MapView,
add a GraphicsLayer,
display the Sketch widget directly on the map.
In order to better manage screen space, I then tried to place the Sketch widget inside an Expand widget.
This is where the difficulties started.
Most of the time, the Expand appears empty
After several attempts, I managed to make the Sketch widget appear inside the Expand
However, this then leads to DOM-related errors on the React side, such as:
Uncaught NotFoundError: Failed to execute 'insertBefore' on 'Node':
The node before which the new node is to be inserted is not a child of this node.or:
Uncaught DOMException: Node.insertBefore: Child to insert before is not a child of this node
React 32
performWorkUntilDeadline scheduler.development.js:45
react-dom_client.js:9714:50These errors usually occur:
after a React re-render,
or during the mount / unmount lifecycle of the Sketch widget,
even though the widget initially appears to work correctly inside the Expand.
React 19.0.0
Vite 7.3.0
TypeScript
ArcGIS JS API 4.34
Usage of useEffect, useRef
Sketch widget instantiated using the container property
It seems I am facing a conflict between ArcGIS’ imperative DOM management and React’s virtual DOM, especially when the widget is hosted inside an Expand.
Is there:
an officially recommended Esri pattern to properly integrate the Sketch widget inside an Expand in a React environment?
a known limitation or documented constraint regarding Expand + React?
a best practice to avoid these insertBefore errors in this specific scenario?
Here is the relevant code:
// Widget Sketch
useEffect(() => {
const view = viewRef.current;
const panel = stylerPanelRef.current;
const layer = sketchLayerRef.current;
if (!view || !panel || !layer) return;
// créer le container Sketch
const sketchContainer = document.createElement("div");
sketchContainer.className =
"w-full h-auto border border-slate-300 dark:border-slate-700 rounded-md mb-2";
// point d’ancrage STABLE géré par React (le bouton)
const clearButton = panel.querySelector("button");
if (clearButton && clearButton.parentNode === panel) {
// solution StackOverflow : insertBefore(parent, nextSibling)
panel.insertBefore(sketchContainer, clearButton);
} else {
// fallback ultra-sécurisé
panel.appendChild(sketchContainer);
}
// init Sketch
const sketch = new Sketch({
view,
layer,
container: sketchContainer,
creationMode: "single",
visibleElements: {
createTools: {
point: false,
polyline: true,
polygon: true,
rectangle: true,
circle: true,
},
selectionTools: {
"lasso-selection": true,
"rectangle-selection": true,
},
settingsMenu: true,
},
});
const createHandle = sketch.on("create", (event) => {
if (event.state === "complete" && event.graphic?.geometry) {
selectBySketchGeometry(event.graphic.geometry);
}
});
sketchWidgetRef.current = sketch;
return () => {
createHandle.remove();
sketch.destroy();
sketchWidgetRef.current = null;
// nettoyage DOM sécurisé
if (panel.contains(sketchContainer)) {
panel.removeChild(sketchContainer);
}
};
}, [selectBySketchGeometry]);JSX:
{/* Styler Panel (utilisé par le widget Expand ArcGIS) */}
<div ref={stylerPanelRef} className="p-3 space-y-2 text-sm bg-white dark:bg-slate-800 h-auto">
<h3 className="font-bold text-slate-700 dark:text-slate-200">Sketch Styler</h3>
{/* Le conteneur Sketch est ajouté dynamiquement via JavaScript */}
<button
type="button"
className="px-3 py-1.5 text-sm font-medium text-white transition-colors bg-blue-600 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-slate-800"
onClick={() => sketchLayerRef.current?.removeAll()}
>
Clear Sketches
</button>
</div>Thank you in advance for your help and feedback.