Closed hojberg closed 9 years ago
need a way to bind events to body/document in react way, because react will batch setState in event handler, it is very useful.
such as:
React.createClass({
onDocumentEventType:function(){
}
})
You can use React.addons.batchedUpdates to get the same functionality.
@spicyj
It's not in docs? http://facebook.github.io/react/docs/addons.html
Better make it into core, we usually just use react core.
Ah, yes. #3570
As for this issue generally, see #1608. We're not planning to add this event. For many cases with popups and modals, you can add a backdrop element (either transparent or not) to capture clicks. For others, you can add a top-level listener to the document and catch clicks. #285 tracks adding a more complete solution to that, though for now manually calling addEventListener for that case is probably best.
Could we at least document here that React events are handled after all the native DOM events are handled?
@polkovnikov-ph That's not necessarily true – it depends when the other listeners are added.
Following @gaearon and @willdady suggestions a very simple DropDown implementation could be like the following one:
Note that the DropDown remains open when user clicks on input field which is outside the DropDown.
DropDown.js
import React, { Component, PropTypes } from 'react';
class DropDown extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
componentDidMount() {
document.addEventListener('click', this.props.onBodyClick);
}
componentWillUnmount() {
document.removeEventListener('click', this.props.onBodyClick);
}
handleClick(e) {
e.nativeEvent.stopImmediatePropagation();
}
render() {
return (
<div onClick={this.handleClick}>
{this.props.children}
</div>
);
}
}
DropDown.propTypes = {
children: PropTypes.node,
onBodyClick: PropTypes.func
};
DropDown.defaultProps = {
onBodyClick: () => {}
};
export default DropDown;
MyComponent.js
import React, { Component, PropTypes } from 'react';
import cx from 'classnames';
import DropDown from './DropDown';
class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
isMenuOpen: false
};
this.handleInputClick = this.handleInputClick.bind(this);
this.handleBodyClick = this.handleBodyClick.bind(this);
}
handleInputClick() {
this.setState({ isMenuOpen: true });
}
handleBodyClick() {
if (document.activeElement === this.textInput) {
return;
}
this.setState({ isMenuOpen: false });
}
render() {
return (
<div>
<input
ref={(ref) => { this.textInput = ref; }}
onClick={this.handleInputClick}
/>
<DropDown onBodyClick={this.handleBodyClick}>
<ul className={cx({ 'DropDown--hidden': !this.state.isMenuOpen })}>
<li>Option 1</li>
<li>Option 2</li>
<li>Option 3</li>
</ul>
</DropDown>
</div>
);
}
}
export default MyComponent;
Also note that the react-onclickoutside library has seen tons of contributions from some 24 people over the last 8 months and has been on a constant modernization cycle, so as of this comment it's at v5.7, and implemented as Higher Order Component that can turn anything you throw at it into a component with onclickoutside monitoring, making dropdown menus really easy to implement.
@johndous I think this solution only works on desktop, what about mobile devices and touch events which do not trigger click events. Any suggestions on how to handle this?
Add the 'clickoutside' event similar to this jquery plugin: https://github.com/cowboy/jquery-outside-events
Very useful sugar for components like dropdowns and modals that need to close/hide when you click out side the container.