Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Attaching event to document, that depends on conditionally rendered element in React

I have an input element, that is rendered depending on condition.

render() {
const {
  isNameInputVisible,
  name
} = this.state;

return (

    <div>
      {isNameInputVisible ? (
        <input
          onChange={this.handleNameChange}
          ref={this.nameInput}
          type="text"
          value={name}
        />
      ) : (
        <h1
          className="list-header__heading"
          onClick={this.handleNameInputVisibility}
        >
          {name}
        </h1>
      )}
    </div>
)

Basically, I want to listen for a click on the document, to hide the input whenever user click's outside this input element.

Right now I'm doing this:

  componentDidMount() {
    document.addEventListener('mousedown', this.handleClick);
  }


  handleClick = event => {
      //do some logic
  };

But I've been wondering if this is the correct way, because the event exists and fires, even when the element is not rendered.

So I've tried this:

componentDidUpdate () {
   const {isNameInputVisible} = this.state;
    isNameInputVisible &&  document.addEventListener('mousedown', this.handleClick);
}

But it doesn't work.

Question 1: What is the right way of attaching events to document when it depends on other conditionally rendered elements??

Question 2: What is the correct way of attaching events, for example, like escape press, for closing dialogs that o etc??

like image 208
p7adams Avatar asked Nov 19 '25 02:11

p7adams


1 Answers

You need to add an event listener in the componentDidMount method only if the conditionally rendered element's ref exists. You can tell if a ref has been attached to an element by using this.refName.current.

The most important thing here is that the input element gets its own lifecycle methods instead of sharing them with a larger component. By moving the input element to its own component with its own lifecycle methods, those methods will only fire around the creation and removal of the input.

// App.jsx
import React from "react"
import ReactDOM from "react-dom"

import CustomInput from "./CustomInput"

class App extends React.Component {
  constructor(props) {
    super(props)
    this.inputRef = React.createRef()
    this.toggleInput = this.toggleInput.bind(this)
    this.state = {
      inputVisible: false
    }
  }

  toggleInput(e) {
    this.setState(prevState => ({
      inputVisible: !prevState.inputVisible
    }))
  }

  render() {
    const { inputVisible } = this.state

    return (
      <div>
        <input type="button" value="toggle input" onClick={this.toggleInput} />
        { inputVisible
          ? <CustomInput />
          : <p>Input is not visible</p>
        }
      </div>
    )
  }
}

const rootElement = document.getElementById("root")
ReactDOM.render(<App />, rootElement)
// CustomInput.jsx
import React from "react"

export default class CustomInput extends React.Component {
  constructor(props) {
    super(props)
    this.inputRef = React.createRef()
  }

  componentDidMount() {
    this.inputRef.current &&
      document.addEventListener("mousedown", this.handleClick)
  }

  componentWillUnmount() {
    document.removeEventListener("mousedown", this.handleClick)
  }

  handleClick(e) {
    console.log("clicked")
  }

  render() {
    return (
      <input type="text" ref={this.inputRef} />
    )
  }
}

Try it here

like image 193
Adam Avatar answered Nov 21 '25 15:11

Adam



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!