Skip to content
This repository has been archived by the owner on Mar 4, 2020. It is now read-only.

Commit

Permalink
chore(EventListener): use hooks (#1755)
Browse files Browse the repository at this point in the history
* chore(EventListener): use hooks

* export hook

* add changelog entry

* Update CHANGELOG.md

Co-Authored-By: Pavel Lučivňák <[email protected]>

* fix format

* fix UTs

* add comment
  • Loading branch information
layershifter authored Aug 8, 2019
1 parent 7e1443f commit f9e53a4
Show file tree
Hide file tree
Showing 18 changed files with 108 additions and 358 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

### BREAKING CHANGES
- Fix `firstFocusableSelector` in `FocusTrapZone` and `AutoFocusZone` @sophieH29 ([#1732](https://github.com/stardust-ui/react/pull/1732))
- `StackableEventListener` is removed @layershifter ([#1755](https://github.com/stardust-ui/react/pull/1755))
- Rename `Tree` to `HierarchicalTree` @silviuavram ([#1752](https://github.com/stardust-ui/react/pull/1752))

### Fixes
Expand All @@ -35,6 +36,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Export `call-blocked` icon to Teams theme @francescopalmiotto ([#1736](https://github.com/stardust-ui/react/pull/1736))
- Add support for component styles debugging @kuzhelov ([#1726](https://github.com/stardust-ui/react/pull/1726))
- Use FocusZone in selectable list @jurokapsiar ([#1757](https://github.com/stardust-ui/react/pull/1757))
- Add `useEventListener` hook @layershifter ([#1755](https://github.com/stardust-ui/react/pull/1755))

### Documentation
- Fix code in changing component variables section of theming examples @lucivpav ([#1626](https://github.com/stardust-ui/react/pull/1626))
Expand Down
55 changes: 22 additions & 33 deletions packages/react-component-event-listener/src/EventListener.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,29 @@
import * as React from 'react'
import * as PropTypes from 'prop-types'

import addEventListener from './lib/addEventListener'
import removeEventListener from './lib/removeEventListener'
import shouldUpdateListener from './lib/shouldUpdateListener'
import { EventListenerProps } from './types'
import { listenerPropTypes } from './types.internal'
import useEventListener from './useEventListener'
import { EventListenerOptions, EventTypes, TargetRef } from './types'

class EventListener extends React.Component<EventListenerProps> {
static displayName = 'EventListener'
static propTypes = listenerPropTypes
static defaultProps = {
capture: false,
}
function EventListener<T extends EventTypes>(props: EventListenerOptions<T>) {
useEventListener(props)

shouldComponentUpdate(nextProps: EventListenerProps) {
return shouldUpdateListener(this.props, nextProps)
}

componentDidMount() {
addEventListener(this.handleEvent, this.props as Required<EventListenerProps>)
}

componentDidUpdate(prevProps: EventListenerProps) {
removeEventListener(this.handleEvent, prevProps as Required<EventListenerProps>)
addEventListener(this.handleEvent, this.props as Required<EventListenerProps>)
}

componentWillUnmount() {
removeEventListener(this.handleEvent, this.props as Required<EventListenerProps>)
}

handleEvent = (e: Event) => this.props.listener(e)
return null
}

render() {
return null
}
EventListener.displayName = 'EventListener'
// TODO: use Babel plugin for this
EventListener.propTypes =
process.env.NODE_ENV !== 'production'
? {
capture: PropTypes.bool,
listener: PropTypes.func.isRequired,
targetRef: PropTypes.shape({
current: PropTypes.object,
}).isRequired as PropTypes.Validator<TargetRef>,
type: PropTypes.string.isRequired as PropTypes.Validator<EventTypes>,
}
: {}
EventListener.defaultProps = {
capture: false,
}

export default EventListener

This file was deleted.

9 changes: 0 additions & 9 deletions packages/react-component-event-listener/src/hooks/types.ts

This file was deleted.

This file was deleted.

This file was deleted.

2 changes: 1 addition & 1 deletion packages/react-component-event-listener/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ export const windowRef: TargetRef = {
}

export { default as EventListener } from './EventListener'
export { default as StackableEventListener } from './StackableEventListener'
export * from './types'
export { default as useEventListener } from './useEventListener'

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

17 changes: 0 additions & 17 deletions packages/react-component-event-listener/src/types.internal.ts

This file was deleted.

8 changes: 4 additions & 4 deletions packages/react-component-event-listener/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import * as React from 'react'

export interface EventListenerProps {
/** Idicating that events of this type will be dispatched to the registered listener before being dispatched to any EventTarget beneath it in the DOM tree. */
export interface EventListenerOptions<T extends EventTypes = 'click'> {
/** Indicating that events of this type will be dispatched to the registered listener before being dispatched to any EventTarget beneath it in the DOM tree. */
capture?: boolean

/** A function which receives a notification when an event of the specified type occurs. */
listener: EventHandler<EventTypes>
listener: EventHandler<T>

/** A ref object with a target node. */
targetRef: TargetRef

/** A case-sensitive string representing the event type to listen for. */
type: EventTypes
type: T
}

export type EventHandler<T extends EventTypes> = (e: DocumentEventMap[T]) => void
Expand Down
41 changes: 41 additions & 0 deletions packages/react-component-event-listener/src/useEventListener.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import * as React from 'react'

import { EventHandler, EventListenerOptions, EventTypes, TargetRef } from './types'

const isActionSupported = (
targetRef: TargetRef,
method: 'addEventListener' | 'removeEventListener',
) => targetRef && !!targetRef.current && !!targetRef.current[method]

const useEventListener = <T extends EventTypes>(options: EventListenerOptions<T>): void => {
const { capture, listener, type, targetRef } = options

const latestListener = React.useRef<EventHandler<T>>(listener)
latestListener.current = listener

const eventHandler = React.useCallback((event: DocumentEventMap[T]) => {
return latestListener.current(event)
}, [])

React.useEffect(() => {
if (isActionSupported(targetRef, 'addEventListener')) {
;(targetRef.current as NonNullable<Node>).addEventListener(type, eventHandler, capture)
} else if (process.env.NODE_ENV !== 'production') {
throw new Error(
'@stardust-ui/react-component-event-listener: Passed `targetRef` is not valid or does not support `addEventListener()` method.',
)
}

return () => {
if (isActionSupported(targetRef, 'removeEventListener')) {
;(targetRef.current as NonNullable<Node>).removeEventListener(type, eventHandler, capture)
} else if (process.env.NODE_ENV !== 'production') {
throw new Error(
'@stardust-ui/react-component-event-listener: Passed `targetRef` is not valid or does not support `removeEventListener()` method.',
)
}
}
}, [capture, targetRef, type])
}

export default useEventListener
Loading

0 comments on commit f9e53a4

Please sign in to comment.