Unmount React Elementts

I am using the external renderer config option to render React components.

 gantt.config.external_render = {
    // checks the element is a React element
    isElement: (element) => {
      return React.isValidElement(element);
    },
    // renders the React element into the DOM
    renderElement: (element, container) => {
      const root = createRoot(container);
      root.render(element);
    },
  };

This works fine however I am using smart rendering and these components never get unmounted which eventually causes the entire site to slow down. How can I call root.unmount() when the row is no longer visible.

Hello,
When Gantt renders the data, the previous HTML elements are removed and Gantt creates new HTML elements. Then it calls the onrender function that is used to tell React to render its elements. So, at the time that function is called, there is no way to get the previous HTML elements.
You need to unmount React elements in the onBeforeDataRender event handler:
https://docs.dhtmlx.com/gantt/api__gantt_onbeforedatarender_event.html

Here is an example:

    gantt.attachEvent("onBeforeDataRender", function () {
      var nodes = document.querySelectorAll(`.gantt_cell[data-column-index="3"]`)
      nodes.forEach(function (node) {
        ReactDOM.unmountComponentAtNode(node);
      });
    });

And here is a React demo:
https://files.dhtmlx.com/30d/5461a91d74c4e089a1c82942dcecd19a/reactive-gantt+gantt-instance+react-components.zip

Thank you for the response but it seems that your code does not work correctly. The onBeforeDataRender only gets triggered when the entire table gets rerendered. The function that was commented out to manually unmount does work so I think the event to attach it to needs to be altered.

Hello,
Yes, the onBeforeDataRender event fires only when Gantt needs to repaint more than one task. But Gantt doesn’t have a universal event that fires before Gantt starts repainting a single task. There is no easy solution for that use case.

You need to detach React elements by using different event handlers. For example, the task is repainted when you click on a task. But you need to use the onBeforeTaskSelected event handler because the onTaskClick event fires after the data is repainted.
While dragging a task, Gantt calls the onTaskDrag event. You can detach React elements there.

The onBeforeDataRender event fires in the following cases:
• when you load tasks with the parse or load method
• when you load child tasks by expanding their parent task and branch_loading is enabled
• when you collapse or expand tasks
• when you close the lightbox
• when you delete a task
• when you vertically reorder a task
• when the size of the Gantt container is increased/decreased
• when Gantt is initialized
• when a task is auto-scheduled(because Gantt repaints several tasks)
• when you use the following methods:

  • render
  • resetLayout
  • batchUpdate
  • addTask
  • addLinkclearAll
  • close
  • open
  • expand
  • collapse
  • createTask
  • deleteTask
  • exportToExcel
  • exportToICal
  • exportToJSON
  • exportToMSProject
  • exportToPDF
  • exportToPNG
  • exportToPrimaveraP6
  • groupBy
  • importFromExcel
  • importFromMSProject
  • importFromPrimaveraP6
  • init
  • deleteTask
  • undo
  • redo
  • refreshData
  • sort
  • autoSchedule

And still, it won’t work in all cases. For example, Gantt repaints the whole task row when you start creating a link. But there is no event for that.

I added a feature request to our internal bug tracker to add an event that will fire before Gantt starts repainting a task. The dev team will add it in the future, but I cannot give you any ETA.

For now, you need to detach React elements in different event handlers. It is not a complete solution, but at least it will make the things better:

    function detachReactNodes(){
      var nodes1 = document.querySelectorAll(`.gantt_cell[data-column-index="3"]`)
      nodes1.forEach(function (node) {
        ReactDOM.unmountComponentAtNode(node);
      });
      var nodes2 = document.querySelectorAll(`.gantt_cell[data-column-index="4"]`)
      nodes2.forEach(function (node) {
        ReactDOM.unmountComponentAtNode(node);
      });
    }

    function detachSpecificReactNode(id) {
      ReactDOM.unmountComponentAtNode(document.querySelector(`[data-task-id="${id}"] .gantt_cell[data-column-index="3"]`));
      ReactDOM.unmountComponentAtNode(document.querySelector(`[data-task-id="${id}"] .gantt_cell[data-column-index="4"]`));
    }

    gantt.attachEvent("onBeforeDataRender", function () {
      detachReactNodes();
    });

    gantt.attachEvent("onBeforeTaskSelected", function (id, e) {
      detachSpecificReactNode(id);
      return true;
    });
    gantt.attachEvent("onTaskDrag", function (id, mode, task, original) {
      detachSpecificReactNode(id);
    });

    let linkAdd = null;
    gantt.attachEvent("onMouseMove", function (id, e) {
        if (document.querySelector(".gantt_link_direction") && linkAdd === null) {
            detachSpecificReactNode(id);
            linkAdd = "started";
        }
        if (linkAdd && !document.querySelector(".gantt_link_direction")) {
            linkAdd = null;
        }
    });

Here is the updated React demo:
https://files.dhtmlx.com/30d/bcac2d270e7cf8f74f9271d74ffc489b/reactive-gantt+gantt-instance+react-components.zip