Skip to content

Instantly share code, notes, and snippets.

@TylerK
Last active May 8, 2018 10:47
Show Gist options
  • Save TylerK/f85cdde98ed61d4fd31cadf378e4ec62 to your computer and use it in GitHub Desktop.
Save TylerK/f85cdde98ed61d4fd31cadf378e4ec62 to your computer and use it in GitHub Desktop.
Click outside higher order component for React written in TypeScript
import React from 'react';
import { findDOMNode } from 'react-dom';
type HocWrapper<P> = React.ComponentClass<P> | React.SFC<P>;
function withClickOutside<P, S>(Component: HocWrapper<P>): React.ComponentClass<P> {
class WrappedComponent extends React.Component<P, S> {
private handleClickOutside = e => {
const bounds = this.wrappedDomNode.getBoundingClientRect();
const mouseX = e.clientX || e.pageX;
const mouseY = e.clientY || e.pageY;
const clickedInBounds =
mouseX <= bounds.right &&
mouseX >= bounds.left &&
mouseY <= bounds.bottom &&
mouseY >= bounds.top;
if (!clickedInBounds && typeof this.wrappedInstance.handleClickOutside === 'function') {
this.wrappedInstance.handleClickOutside(e);
}
};
public static displayName = '';
public wrappedInstance = null;
public wrappedDomNode = null;
public componentDidMount() {
window.addEventListener('click', this.handleClickOutside, true);
}
public componentWillUnmount() {
window.removeEventListener('click', this.handleClickOutside, true);
}
// Explicitely typing the return here keeps TS from yelling. ¯\_(ツ)_/¯
public render(): JSX.Element {
const { ...props } = this.props as any;
return (
<Component
{...props}
ref={el => {
this.wrappedInstance = el;
this.wrappedDomNode = findDOMNode(el);
}}
/>
);
}
}
const name = WrappedComponent.displayName || WrappedComponent.name;
WrappedComponent.displayName = `withClickOutside(${name})`;
return WrappedComponent;
}
export default withClickOutside;
import React from 'react';
import withClickOutside from './click-outside';
class Widget extends React.Component<any, any> {
handleClickOutside = e => {
console.log('Clicked outside!');
}
handleClick = e => {
console.log('Clicked inside!');
}
render() {
return <button onClick={this.handleClick}>Click Me!</button>
}
}
export default withClickOutside(Widget);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment