Skip to main content

Media

Media Query

export default function useMedia<T>(
queries: string[],
values: T[],
defaultValue: T
) {
// Array containing a media query list for each query
const mediaQueryLists = queries.map(q => window.matchMedia(q))

const getValue = () => {
// Get index of first media query that matches
const index = mediaQueryLists.findIndex(mql => mql.matches)
return values?.[index] || defaultValue
}

// State and setter for matched value
const [value, setValue] = useState<T>(getValue)

useMount(() => {
const handler = () => setValue(getValue)
mediaQueryLists.forEach(mql => mql.addListener(handler))
return () => mediaQueryLists.forEach(mql => mql.removeListener(handler))
})

return value
}

Locked Body

import { useEffect, useLayoutEffect, useState } from 'react'

type ReturnType = [boolean, (locked: boolean) => void]

function useLockedBody(initialLocked = false): ReturnType {
const [locked, setLocked] = useState(initialLocked)

// Do the side effect before render
useLayoutEffect(() => {
// Key point 1
if (!locked)
return

// Save initial body style
const originalOverflow = document.body.style.overflow
const originalPaddingRight = document.body.style.paddingRight

// Lock body scroll
document.body.style.overflow = 'hidden'

// Get the scrollBar width
const root = document.getElementById('___gatsby') // or root
const scrollBarWidth = root ? root.offsetWidth - root.scrollWidth : 0

// Avoid width reflow
if (scrollBarWidth)
document.body.style.paddingRight = `${scrollBarWidth}px`

// Key point 2
return () => {
document.body.style.overflow = originalOverflow

if (scrollBarWidth)
document.body.style.paddingRight = originalPaddingRight
}
}, [locked])

// Update state if initialValue changes
useEffect(() => {
if (locked !== initialLocked) {
setLocked(initialLocked)
}
}, [initialLocked])

return [locked, setLocked]
}

export default useLockedBody

Mouse

import { useRef, useState } from 'react'

export default function useLongPress(time = 500) {
const [action, setAction] = useState()

const timerRef = useRef()
const isLongPress = useRef()

function startPressTimer() {
isLongPress.current = false
timerRef.current = setTimeout(() => {
isLongPress.current = true
setAction('LongPress')
}, time)
}

function handleClick() {
if (isLongPress.current)
return

setAction('Click')
}

function handleMouseDown() {
startPressTimer()
}

function handleMouseUp() {
clearTimeout(timerRef.current)
}

function handleTouchStart() {
startPressTimer()
}

function handleTouchEnd() {
if (action === 'LongPress')
return

clearTimeout(timerRef.current)
}

return {
action,
handlers: {
onClick: handleClick,
onMouseDown: handleMouseDown,
onMouseUp: handleMouseUp,
onTouchStart: handleTouchStart,
onTouchEnd: handleTouchEnd,
},
}
}