Select to view content in your preferred language

Sketch widget in Expand breaks React DOM lifecycle (ArcGIS JS API + React)

230
0
01-30-2026 05:35 PM
Med-Karim-Rouissi
Occasional Contributor

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.


Issue encountered

  • 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:50

These 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.

 

Technical context

  • 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.

Question

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.

0 Kudos
0 Replies