Skip to content

Commit

Permalink
feat(useViewportState): introduces useViewportState
Browse files Browse the repository at this point in the history
  • Loading branch information
Antonio Rù committed Jun 13, 2022
1 parent 293e1f4 commit d071fea
Show file tree
Hide file tree
Showing 16 changed files with 114 additions and 2 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ $ yarn add beautiful-react-hooks
* [useMediaQuery](docs/useMediaQuery.md)
* [useOnlineState](docs/useOnlineState.md)
* [useViewportSpy](docs/useViewportSpy.md)
* [useViewportState](docs/useViewportState.md)
* [useSpeechSynthesis](docs/useSpeechSynthesis.md) and [useSystemVoices](docs/useSystemVoices.md)
* [useGeolocation](docs/useGeolocation.md), [useGeolocationState](docs/useGeolocationState.md)
and [useGeolocationEvents](docs/useGeolocationEvents.md)
Expand Down
1 change: 1 addition & 0 deletions docs/README.es-ES.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ $ yarn add beautiful-react-hooks
* [useMediaQuery](useMediaQuery.md)
* [useOnlineState](useOnlineState.md)
* [useViewportSpy](useViewportSpy.md)
* [useViewportState](useViewportState.md)
* [useSpeechSynthesis](useSpeechSynthesis.md) and [useSystemVoices](useSystemVoices.md)
* [useGeolocation](useGeolocation.md), [useGeolocationState](useGeolocationState.md) and [useGeolocationEvents](useGeolocationEvents.md)
* [useDrag](useDrag.md), [useDropZone](useDropZone.md) and [useDragEvents](useDragEvents.md)
Expand Down
1 change: 1 addition & 0 deletions docs/README.it-IT.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ $ yarn add beautiful-react-hooks
* [useMediaQuery](useMediaQuery.md)
* [useOnlineState](useOnlineState.md)
* [useViewportSpy](useViewportSpy.md)
* [useViewportState](useViewportState.md)
* [useSpeechSynthesis](useSpeechSynthesis.md) and [useSystemVoices](useSystemVoices.md)
* [useGeolocation](useGeolocation.md), [useGeolocationState](useGeolocationState.md) and [useGeolocationEvents](useGeolocationEvents.md)
* [useDrag](useDrag.md), [useDropZone](useDropZone.md) and [useDragEvents](useDragEvents.md)
Expand Down
1 change: 1 addition & 0 deletions docs/README.jp-JP.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ $ yarn add beautiful-react-hooks
* [useMediaQuery](useMediaQuery.md)
* [useOnlineState](useOnlineState.md)
* [useViewportSpy](useViewportSpy.md)
* [useViewportState](useViewportState.md)
* [useSpeechSynthesis](useSpeechSynthesis.md) and [useSystemVoices](useSystemVoices.md)
* [useGeolocation](useGeolocation.md), [useGeolocationState](useGeolocationState.md) and [useGeolocationEvents](useGeolocationEvents.md)
* [useDrag](useDrag.md), [useDropZone](useDropZone.md) and [useDragEvents](useDragEvents.md)
Expand Down
1 change: 1 addition & 0 deletions docs/README.pl-PL.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ $ yarn add beautiful-react-hooks
* [useMediaQuery](useMediaQuery.md)
* [useOnlineState](useOnlineState.md)
* [useViewportSpy](useViewportSpy.md)
* [useViewportState](useViewportState.md)
* [useSpeechSynthesis](useSpeechSynthesis.md) and [useSystemVoices](useSystemVoices.md)
* [useGeolocation](useGeolocation.md), [useGeolocationState](useGeolocationState.md) and [useGeolocationEvents](useGeolocationEvents.md)
* [useDrag](useDrag.md), [useDropZone](useDropZone.md) and [useDragEvents](useDragEvents.md)
Expand Down
1 change: 1 addition & 0 deletions docs/README.pt-BR.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ $ yarn add beautiful-react-hooks
* [useMediaQuery](useMediaQuery.md)
* [useOnlineState](useOnlineState.md)
* [useViewportSpy](useViewportSpy.md)
* [useViewportState](useViewportState.md)
* [useSpeechSynthesis](useSpeechSynthesis.md) and [useSystemVoices](useSystemVoices.md)
* [useGeolocation](useGeolocation.md), [useGeolocationState](useGeolocationState.md) and [useGeolocationEvents](useGeolocationEvents.md)
* [useDrag](useDrag.md), [useDropZone](useDropZone.md) and [useDragEvents](useDragEvents.md)
Expand Down
1 change: 1 addition & 0 deletions docs/README.uk-UA.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ $ yarn add beautiful-react-hooks
* [useMediaQuery](useMediaQuery.md)
* [useOnlineState](useOnlineState.md)
* [useViewportSpy](useViewportSpy.md)
* [useViewportState](useViewportState.md)
* [useSpeechSynthesis](useSpeechSynthesis.md) and [useSystemVoices](useSystemVoices.md)
* [useGeolocation](useGeolocation.md), [useGeolocationState](useGeolocationState.md) and [useGeolocationEvents](useGeolocationEvents.md)
* [useDrag](useDrag.md), [useDropZone](useDropZone.md) and [useDragEvents](useDragEvents.md)
Expand Down
1 change: 1 addition & 0 deletions docs/README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ $ yarn add beautiful-react-hooks
* [useMediaQuery](useMediaQuery.md)
* [useOnlineState](useOnlineState.md)
* [useViewportSpy](useViewportSpy.md)
* [useViewportState](useViewportState.md)
* [useSpeechSynthesis](useSpeechSynthesis.md) and [useSystemVoices](useSystemVoices.md)
* [useGeolocation](useGeolocation.md), [useGeolocationState](useGeolocationState.md) and [useGeolocationEvents](useGeolocationEvents.md)
* [useDrag](useDrag.md), [useDropZone](useDropZone.md) and [useDragEvents](useDragEvents.md)
Expand Down
38 changes: 38 additions & 0 deletions docs/useViewportState.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# useViewportState

Returns information on the current viewport state

It's built on top of [useWindowResize](./useWindowResize.md) and [useWindowScroll](./useWindowScroll.md).

### Why? 💡

- takes care of adding the listener for the window resize event.
- takes care of removing the listener when the component will unmount

### Basic Usage:

```jsx harmony
import { useState } from 'react';
import useViewportState from 'beautiful-react-hooks/useViewportState';

const WindowSizeReporter = () => {
const { width, height, scrollX, scrollY } = useViewportState();

return (
<DisplayDemo>
<p>window width: {width}</p>
<p>window height: {height}</p>
<p>window scrollX: {scrollX}</p>
<p>window scrollY: {scrollY}</p>
</DisplayDemo>
);
};

<WindowSizeReporter />
```

### Mastering the hook

#### ✅ When to use

- When in need of reading common information about the current viewport
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,4 @@ export { default as useInfiniteScroll } from './useInfiniteScroll'
export { default as useQueryParam } from './useQueryParam'
export { default as useSearchQuery } from './useSearchQuery'
export { default as useEvent } from './useEvent'
export { default as useViewportState } from './useViewportState'
5 changes: 5 additions & 0 deletions src/useDebouncedCallback.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useCallback, useEffect, useRef } from 'react'
import debounce from 'lodash.debounce'
import { GenericFunction } from './shared/types'
import useWillUnmount from './useWillUnmount'

export type DebounceOptions = {
leading?: boolean | undefined;
Expand All @@ -26,6 +27,10 @@ const useDebouncedCallback = <TCallback extends GenericFunction>
debounced.current = debounce(fn, wait, options)
}, [fn, wait, options])

useWillUnmount(() => {
debounced.current?.cancel()
})

return useCallback(debounced.current, dependencies)
}

Expand Down
5 changes: 5 additions & 0 deletions src/useThrottledCallback.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useCallback, useEffect, useRef } from 'react'
import throttle from 'lodash.throttle'
import { GenericFunction } from './shared/types'
import useWillUnmount from './useWillUnmount'

interface ThrottleSettings {
leading?: boolean | undefined;
Expand All @@ -25,6 +26,10 @@ const useThrottledCallback = <TCallback extends GenericFunction>
throttled.current = throttle(fn, wait, options)
}, [fn, wait, options])

useWillUnmount(() => {
throttled.current?.cancel()
})

return useCallback(throttled.current, dependencies)
}

Expand Down
39 changes: 39 additions & 0 deletions src/useViewportState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { useState } from 'react'
import useWindowScroll from './useWindowScroll'
import useWindowResize from './useWindowResize'
import useThrottledCallback from './useThrottledCallback'
import useDidMount from './useDidMount'

export interface ViewportState {
width: number,
height: number,
scrollX: number,
scrollY: number,
}

/**
* Returns updated information on the current viewport state
*/
const useViewportState = (debounceBy: number = 250) => {
const [viewport, setViewport] = useState<ViewportState>({ width: 0, height: 0, scrollY: 0, scrollX: 0 })
const onScroll = useWindowScroll()
const onResize = useWindowResize()
const onMount = useDidMount()

const saveInfo = useThrottledCallback(() => {
setViewport({
width: window.innerWidth,
height: window.innerHeight,
scrollX: window.scrollX,
scrollY: window.scrollY,
})
}, [setViewport], debounceBy)

onScroll(saveInfo)
onResize(saveInfo)
onMount(saveInfo)

return viewport
}

export default useViewportState
2 changes: 1 addition & 1 deletion src/useWindowResize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ import useGlobalEvent from './useGlobalEvent'
/**
* Returns a function that accepts a callback to be performed when the window resize.
*/
const useWindowResize = <TEvent extends UIEvent>() => useGlobalEvent<TEvent>('resize')
const useWindowResize = () => useGlobalEvent<UIEvent>('resize')

export default useWindowResize
2 changes: 1 addition & 1 deletion src/useWindowScroll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ import useGlobalEvent from './useGlobalEvent'
/**
* Returns a function that accepts a callback to be performed when the window scrolls.
*/
const useWindowScroll = <TEvent extends UIEvent>() => useGlobalEvent<TEvent>('scroll')
const useWindowScroll = () => useGlobalEvent<UIEvent>('scroll')

export default useWindowScroll
16 changes: 16 additions & 0 deletions test/useViewportState.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { cleanup, renderHook } from '@testing-library/react-hooks'
import useViewportState from '../dist/useViewportState'
import assertHook from './utils/assertHook'

describe('useViewportState', () => {

beforeEach(() => cleanup())

assertHook(useViewportState)

it('should return an object containing information on the current window state', () => {
const { result } = renderHook(() => useViewportState())

expect(result.current).to.be.an('object').that.has.all.keys('width', 'height', 'scrollY', 'scrollX')
})
})

0 comments on commit d071fea

Please sign in to comment.