1

I have a basic component that looks as follows.

class List extends React.Component {
  constructor() {
    super(...arguments);
    this.state = {
      selected: null,
      entities: new Map([
        [0, { 'name': 'kot'} ],
        [1, { 'name': 'blini'} ]
      ])
    };
  }
  render() {
    return (<div>
      <ul>{this.renderItems()}</ul>
    </div>)
  }
  renderItems() {
    return Array.from(this.state.entities.entries()).map(s => {
      const [ id, entry ] = s;
      return <li
        key={id}
        onClick={() => this.setState(state => ({ selected: id }))}
        style={{
          color: id === this.state.selected ? 'red' : 'black'
        }}
      >{entry.name}</li>
    })
  }
}

This works in order to allow me to click on any element and select it. A selected element will appear red. codepen for easy editing.

However, I want behavior that will unset any currently selected item if a click event was found that was not one of these <li> elements.

How can this be done in React?

corvid
  • 10,733
  • 11
  • 61
  • 130
  • make list item a seperate component like `Item`. Your parent `List` component should have width and height to take more space and implement its own onClick event. – klimat Sep 16 '16 at 19:29

2 Answers2

0

In your List component, You can add

  componentDidMount() {
    window.addEventListener("click", (e) => {
      let withinListItems = ReactDOM.findDOMNode(this).contains(e.target);
      if ( ! withinListItems ) {
        this.setState({ selected: null });  
      }
    });
  }

And in your renderItems, change onClick to

onClick={ (e) => {
    // e.stopPropagation();
    this.setState({ selected: id });
  }
}

You can checkout this codepen http://codepen.io/anon/pen/LRkzWd

Edit:

What @kubajz said is true, and hence i have updated the answer.

Dhruv Kumar Jha
  • 6,421
  • 8
  • 37
  • 48
  • This doesn't remove the event listener when the component is unmounted. (In fact, it can't remove the event listener; because it uses an anonymous local function to `addEventListener`, there's no way to retrieve it to call `removeEventListener`.) You should put the event handler in a class method (e.g., `handleWindowClick`), bind it in the constructor (`this.handleWindowClick = handleWindowClick.bind(this)`), and add a `componentWillUnmount` method that removes the event (`window.removeEventListener("click", this.handleWindowClick)`). – Josh Kelley Sep 16 '16 at 20:28
  • also, it looks like if the parent div is within the scope, it will fail – corvid Sep 16 '16 at 21:55
0

Random User's answer is correct, it may have one flaw - it relies on stopPropagation and there is possibility that some piece of code may no longer work as expected - imagine collecting user's behaviour on page and sending metrics somewhere - stopPropagation will prevent bubbling, thus click is not recorded. Alternative approach is to check what was clicked in event.target: http://codepen.io/jaroslav-kubicek/pen/ORXxkL

Also there is nice utility component for listening on document level: react-event-listener

kubajz
  • 61
  • 7