0

I have a dropdown that is inialized with materialized(does not matter, could be JQuery or any other) and materialized inserts elements in the DOM from whom react is not aware of. This produces that if I use this component as a child of others and render it conditionally, react will throw the exception:

React DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node

This is my dropdowm component, when its not initialized with materialize;

<div class="input-field col s2">
  <select id="defaultOption">
    /* options. Not relevant */
  </select></div>
</div>

But, if initialized (which is needed from the dropdown to work), I got the materialized elements inserted in the DOM but not in the virtual DOM:

<div class="input-field col s2">
  <div class="select-wrapper">        ------>//materialize wrapper
      ....                            ------>//other materialize added nodes
      <select id="defaultOption">     ------>//original select, root node of the component
        /* options. Not relevant */
      </select></div>
  </div>
</div>

Debugging where the error is produced, it is exactly when react, tries to remove the <select id="defaultOption"> in the the function removeChild(parentInstance, child) function, so the mismatch in the DOM hierarchy seems clearly what is causing the problem.

Seems to be very similar to this one, with JQuery.

How can I update those inserted nodes in the virtual DOM, so that there are not mismatches between the DOM and the virtual DOM?

I post below the component code, where you can see how the component is initialized in the unseEffect() in case there could be some help proposal:

import React, { useEffect, useState, useRef } from "react";

const makeOptionItem = function(value: string, key: number) {
  return <option key={key} value={key}>{value}</option>;
};

const MaterializeDropdown = (props: { options: string[], placeholderText:string, onOptionSelect?: (selectedOption:string) => void } ) => {

  useEffect(() => {
    const select = selectRef.current;      
    if (select) M.FormSelect.init(select, {
      dropdownOptions: {
        inDuration: 300,
        outDuration: 225,
        constrainWidth: false, // Does not change width of dropdown to that of the activator
        hover: false, // Activate on hover
        alignment: 'left', // Displays dropdown with edge aligned to the left of button 
        coverTrigger: true
      }
    });
  }, []);
  

  return(
    <select id="defaultOption" defaultValue=''>
      <option value='' disabled>{placeholderText}</option>
      {props.options.map(makeOptionItem)}
    </select>
  );
}

export default MaterializeDropdown;
rustyBucketBay
  • 4,320
  • 3
  • 17
  • 47

1 Answers1

1

Wrapping the select with a div in the return like this just solves the issue:

return(
    <div>
      <select id="defaultOption" defaultValue='' onChange={(event) => handleOnChange(event)} ref={selectRef}>
        <option value='' disabled>{placeholderText}</option>
        {props.options.map(makeOptionItem)}
      </select>
    </div>
    
  );
rustyBucketBay
  • 4,320
  • 3
  • 17
  • 47