React Modal ref Undefined! Can't Add Custom Attribute

5.2k Views Asked by At

I have a simple modal:

renderModalForm() {
    return (
      <Modal
        closeTimeoutMS={150}
        show={this.state.isModalOpen}
        onHide={this.isModalOpen.bind(this)}
      >
        <Modal.Body>
          <div className="close-button-modal">
            <i className="fa fa-times fa-2x pull-right" onClick={this.onButtonClick.bind(this)}></i>
            <div className="clearfix"></div>
          </div>
          <div ref="test" className="testclassname">
          </div>
        </Modal.Body>
      </Modal>
    );
  }

My sole objective is to inject a custom attribute (which unfortunately cannot start with data- or aria- since it's defined by third party) to the div referenced by ref="test"

When I attempt to inject the custom attribute:

someButtonClicked() {
    setTimeout(() => {
      this.setState({
        isModalOpen: true
      })
    }, 100);
    var element = ReactDOM.findDOMNode(this.refs.test);
    element.setAttribute('doku-div', 'form-payment');
}

Here element is always undefined, so setAttribute failed; if I go inspect the element, ref="test" does not exist at the <div> ! Can someone help me as to why this ref is missing inside modal?

3

There are 3 best solutions below

7
On

Try moving your code to ComponentDidMount or ComponentDidUpdate method. Refs shouldn't be undefined there.

You can also use a callback to store a reference to the DOM node:

<Modal.Body>
          <div className="close-button-modal">
            <i className="fa fa-times fa-2x pull-right" onClick={this.onButtonClick.bind(this)}></i>
            <div className="clearfix"></div>
          </div>
          <div ref="{(testNode) => { this.testNode = testNode; }}" className="testclassname">
          </div>
</Modal.Body>

And then use that reference instead of using ReactDOM:

someButtonClicked() {
    setTimeout(() => {
        this.setState({
          isModalOpen: true
        })
    }, 100);

    var element = this.testNode;
    this.testNode.setAttribute('doku-div', 'form-payment');
}
0
On

The correct way to implement is use Callback hook. Whenever the component will render you will have the ref element. useCallback will also help stop unnecessary renders hence better performance.

const Parent = () => {
  const [isVisible, setIsVisible] = useState(false);
  return (
    <>
      <button onClick={() => setIsVisible(!isVisible)}>Show/Hide Popup</button>
      <Child isVisible={isVisible} />
    </>
  );
};

const Child = () => {
  const customRef = useCallback((ref) => {
    // ref element accessible here
  }, []);
  return (
    <Modal show={isVisible}>
      <Modal.Body>
        <input ref={customRef} type="text" />
      </Modal.Body>
    </Modal>
  );
};
0
On

useRef hook will not work in modals as the component will mount but the jsx will not render until you make show prop to true. useRef is asynchronous in nature thats why at the time of declaration it sets current to null but after you assign it to any element ref got value of it. But in case of modals the case is different. Here the elements are not registered instantly but after modal show prop is set to true

To solve this make the modal's show prop always to true and make whole component to show/hide dynamically similar query