diff --git a/README.md b/README.md
index 4c5e860c..3ee6177b 100644
--- a/README.md
+++ b/README.md
@@ -12,6 +12,7 @@
- [`useMap`](./docs/useMap.md)
- Sensors
- [`useBattery`](./docs/useBattery.md)
+ - [`useHover`](./docs/useHover.md)
- [`useSize`](./docs/useSize.md)
- [`useWindowSize`](./docs/useWindowSize.md)
- Side effects
diff --git a/docs/useHover.md b/docs/useHover.md
new file mode 100644
index 00000000..f3760408
--- /dev/null
+++ b/docs/useHover.md
@@ -0,0 +1,25 @@
+# `useHover`
+
+React sensor hook that tracks size of some HTML element.
+
+
+## Usage
+
+```jsx
+import {useHover} from 'react-use';
+
+const Demo = () => {
+ const element = (hovered) =>
+
+ Hover me! {hovered && 'Thanks!'}
+
;
+ const [hoverable, hovered] = useHover(element);
+
+ return (
+
+ {hoverable}
+
{hovered ? 'HOVERED' : ''}
+
+ );
+};
+```
diff --git a/package.json b/package.json
index 27748094..d165dce2 100644
--- a/package.json
+++ b/package.json
@@ -4,6 +4,7 @@
"description": "Collection of React Hooks",
"main": "lib/index.js",
"scripts": {
+ "start": "yarn storybook",
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc",
"test:story:build": "build-storybook",
@@ -23,5 +24,9 @@
"ts-node": "^7.0.1",
"ts-loader": "3",
"babel-core": "^6.26.3"
+ },
+ "peerDependencies": {
+ "react": "16.7.0-alpha.0",
+ "react-dom": "16.7.0-alpha.0"
}
}
diff --git a/src/__stories__/useHover.story.tsx b/src/__stories__/useHover.story.tsx
new file mode 100644
index 00000000..02aa917e
--- /dev/null
+++ b/src/__stories__/useHover.story.tsx
@@ -0,0 +1,23 @@
+import {storiesOf} from '@storybook/react';
+import * as React from 'react';
+import {useHover} from '..';
+
+const Demo = () => {
+ const element = (hovered: boolean) =>
+
+ Hover me! {hovered && 'Thanks!'}
+
;
+ const [hoverable, hovered] = useHover(element);
+
+ return (
+
+ {hoverable}
+
{hovered ? 'HOVERED' : ''}
+
+ );
+};
+
+storiesOf('useHover', module)
+ .add('Example', () =>
+
+ )
diff --git a/src/index.ts b/src/index.ts
index d6640282..15bad294 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,5 +1,6 @@
import useBattery from './useBattery';
import useCounter from './useCounter';
+import useHover from './useHover';
import useList from './useList';
import useMap from './useMap';
import useSize from './useSize';
@@ -10,6 +11,7 @@ import useWindowSize from './useWindowSize';
export {
useBattery,
useCounter,
+ useHover,
useList,
useMap,
useSize,
diff --git a/src/useHover.ts b/src/useHover.ts
new file mode 100644
index 00000000..16e03d5b
--- /dev/null
+++ b/src/useHover.ts
@@ -0,0 +1,32 @@
+import * as React from 'react';
+import {useState} from './react';
+
+const noop = () => {};
+
+export type Element = ((state: boolean) => React.ReactElement) | React.ReactElement;
+
+const useHover = (element: Element): [React.ReactElement, boolean] => {
+ const [state, setState] = useState(false);
+
+ const onMouseEnter = (originalOnMouseEnter?: any) => (event: any) => {
+ (originalOnMouseEnter || noop)(event);
+ setState(true);
+ };
+ const onMouseLeave = (originalOnMouseLeave?: any) => (event: any) => {
+ (originalOnMouseLeave || noop)(event);
+ setState(false);
+ };
+
+ if (typeof element === 'function') {
+ element = element(state);
+ }
+
+ const el = React.cloneElement(element, {
+ onMouseEnter: onMouseEnter(element.props.onMouseEnter),
+ onMouseLeave: onMouseLeave(element.props.onMouseLeave)
+ });
+
+ return [el, state];
+};
+
+export default useHover;