Skip to content

Instantly share code, notes, and snippets.

@trezy
Last active November 4, 2020 17:33
Show Gist options
  • Save trezy/86bab00a8c56e8af91a7449348306651 to your computer and use it in GitHub Desktop.
Save trezy/86bab00a8c56e8af91a7449348306651 to your computer and use it in GitHub Desktop.
// Module imports
import {
createContext,
useCallback,
useContext,
useEffect,
useState,
} from 'react'
import PropTypes from 'prop-types'
const KeyStateContext = createContext({
keyState: {},
preventDefaultForKey: () => {},
removeKeyFromPreventDefault: () => {},
})
const KeyStateContextProvider = (props) => {
const { children } = props
const [keyState, setKeyState] = useState({})
const [shouldPreventDefault, setShouldPreventDefault] = useState([])
const handleKeyStateChange = useCallback((event, isPressed) => {
if (shouldPreventDefault.length && ((shouldPreventDefault === 'all') || shouldPreventDefault.includes(event.key))) {
event.preventDefault()
}
setKeyState(previousState => ({
...previousState,
[event.key.toLowerCase()]: isPressed,
}))
}, [setKeyState])
const handleKeydown = useCallback(event => handleKeyStateChange(event, true), [handleKeyStateChange])
const handleKeyup = useCallback(event => handleKeyStateChange(event, false), [handleKeyStateChange])
const preventDefaultForKey = useCallback(key => {
setShouldPreventDefault(previousState => ([
...previousState,
key,
]))
}, [setShouldPreventDefault])
const removeKeyFromPreventDefault = useCallback(key => {
setShouldPreventDefault(previousState => {
return previousState.filter(filterKey => {
const keyType = typeof key
const filterKeyType = typeof filterKey
if (keyType !== filterKeyType) {
return true
}
if ((keyType === 'string') && (filterKeyType === 'string')) {
return key !== filterKey
}
if (Array.isArray(key) && Array.isArray(filterKey)) {
const lengthMatches = key.length === filterKey.length
const keysMatch = key.every(oKey => filterKey.includes(oKey))
return lengthMatches && keysMatch
}
return true
})
})
}, [setShouldPreventDefault])
useEffect(() => {
document.addEventListener('keydown', handleKeydown)
document.addEventListener('keyup', handleKeyup)
return () => {
document.removeEventListener('keydown', handleKeydown)
document.removeEventListener('keyup', handleKeyup)
}
}, [
handleKeydown,
handleKeyup,
])
return (
<KeyStateContext.Provider
value={{
keyState,
preventDefaultForKey,
removeKeyFromPreventDefault,
}}>
{children}
</KeyStateContext.Provider>
)
}
KeyStateContextProvider.propTypes = {
children: PropTypes.node.isRequired,
}
const useKeyState = () => useContext(KeyStateContext)
export {
KeyStateContext,
KeyStateContextProvider,
useKeyState,
}
// Module imports
import {
useEffect,
useState,
} from 'react'
// Local imports
import { useKeyState } from 'context/KeyStateContext'
// Local constants
const KEY_TO_WATCH = 'b'
export function ExampleComponent() {
const {
keyState,
preventDefaultForKey,
removeKeyFromPreventDefault,
} = useKeyState()
const [keyIsPressed, setKeyIsPressed] = useState(keyState[KEY_TO_WATCH])
useEffect(() => {
preventDefaultForKey(KEY_TO_WATCH)
if (keyState[KEY_TO_WATCH]) {
setKeyIsPressed(true)
} else {
setKeyIsPressed(false)
}
return () => removeKeyFromPreventDefault(KEY_TO_WATCH)
}, [keyState])
return (
<div>
Is the 'b' key pressed?<br />
{keyIsPressed ? 'Yes' : 'No'}
</div>
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment