diff --git a/README.md b/README.md index 04781ba94c..a890970942 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,7 @@

- [**Lifecycles**](./docs/Lifecycles.md) + - [`useEvent`](./docs/useEvent.md) — subscribe to events. - [`useLifecycles`](./docs/useLifecycles.md) — calls `mount` and `unmount` callbacks. - [`useRefMounted`](./docs/useRefMounted.md) — tracks if component is mounted. - [`usePromise`](./docs/usePromise.md) — resolves promise only while component is mounted. diff --git a/docs/useEvent.md b/docs/useEvent.md new file mode 100644 index 0000000000..71ffc67740 --- /dev/null +++ b/docs/useEvent.md @@ -0,0 +1,38 @@ +# `useEvent` + +React sensor hook that subscribes a `handler` to events. + + +## Usage + +```jsx +import useEvent from 'react-use/lib/useEvent'; +import useList from 'react-use/lib/useList'; + +const Demo = () => { + const [list, {push}] = useList(); + const onkeydown = ({key}) => { + push(key); + }; + useEvent('keydown', useCallback(onkeydown, [])); + + return ( +
+

+ Press some keys on your keyboard. +

+
+        {JSON.stringify(list, null, 4)}
+      
+
+ ); +}; +``` + + +## Examples + +```js +useEvent('keydown', handler) +useEvent('scroll', handler, window, {useCapture: true}) +``` diff --git a/src/__stories__/useEvent.story.tsx b/src/__stories__/useEvent.story.tsx new file mode 100644 index 0000000000..81f524845b --- /dev/null +++ b/src/__stories__/useEvent.story.tsx @@ -0,0 +1,33 @@ +import {storiesOf} from '@storybook/react'; +import * as React from 'react'; +import {useEvent, useList} from '..'; +import ShowDocs from '../util/ShowDocs'; +import {CenterStory} from './util/CenterStory'; + +const {useCallback} = React; + +const Demo = () => { + const [list, {push, clear}] = useList(); + + useEvent('keydown', useCallback(({key}) => { + if (key === 'r') clear(); + push(key); + }, [])); + + return ( + +

+ Press some keys on your keyboard, r key resets the list +

+
+        {JSON.stringify(list, null, 4)}
+      
+
+ ); +}; + +storiesOf('Sensors|useEvent', module) + .add('Docs', () => ) + .add('Demo', () => + + ) diff --git a/src/__stories__/util/CenterStory.tsx b/src/__stories__/util/CenterStory.tsx new file mode 100644 index 0000000000..c8ab549448 --- /dev/null +++ b/src/__stories__/util/CenterStory.tsx @@ -0,0 +1,15 @@ +import * as React from 'react'; + +export const CenterStory = ({children}) => ( +
+
{children}
+
+); diff --git a/src/index.ts b/src/index.ts index bef549a943..3517665736 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,6 +9,7 @@ import useDropArea from './useDropArea'; import useCounter from './useCounter'; import useCss from './useCss'; import useDebounce from './useDebounce'; +import useEvent from './useEvent'; import useFavicon from './useFavicon'; import useGeolocation from './useGeolocation'; import useGetSet from './useGetSet'; @@ -73,6 +74,7 @@ export { useCounter, useCss, useDebounce, + useEvent, useFavicon, useGeolocation, useGetSet, diff --git a/src/useEvent.ts b/src/useEvent.ts new file mode 100644 index 0000000000..3b43c24362 --- /dev/null +++ b/src/useEvent.ts @@ -0,0 +1,29 @@ +import {useEffect} from 'react'; + +export interface ListenerType1 { + addEventListener (name: string, handler: (event?: any) => void, ...args: any[]); + removeEventListener (name: string, handler: (event?: any) => void); +} + +export interface ListenerType2 { + on (name: string, handler: (event?: any) => void, ...args: any[]); + off (name: string, handler: (event?: any) => void); +} + +export type UseEventTarget = ListenerType1 | ListenerType2; + +const defaultTarget = typeof window === 'object' ? window : null; + +const useEvent = (name: string, handler?: null | undefined | ((event?: any) => void), target: null | UseEventTarget = defaultTarget, options?: any) => { + useEffect(() => { + console.log('adding event...'); + if (!handler) return; + if (!target) return; + ((target as ListenerType1).addEventListener || (target as ListenerType2).on).call(target, name, handler, options); + return () => { + ((target as ListenerType1).removeEventListener || (target as ListenerType2).off).call(target, name, handler, options); + }; + }, [name, handler, target, JSON.stringify(options)]); +}; + +export default useEvent;