Last active
November 4, 2020 17:33
-
-
Save trezy/86bab00a8c56e8af91a7449348306651 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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, | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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