Last active
November 18, 2019 18:16
-
-
Save GoodNovember/e7bfebaefc76c9005306585ce4ea7606 to your computer and use it in GitHub Desktop.
Some Handy React Hooks
This file contains 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
const Range = ({ containerStyle, containerClassName, min, max, value, step, onChange }) => { | |
const animate = (timestamp, delta) => { | |
console.log('AnimationTick', {timestamp, delta}) | |
} | |
const [startAnimation, stopAnimation, getIsAnimatingRef, getIsAnimatingState] = useAnimationFrame(animate, true) | |
useDocumentEvents({ | |
'pointerup' (event) { | |
stopAnimation() | |
}, | |
'pointermove' (event) { | |
if (getIsAnimatingRef()) { | |
console.log('[Document Pointer Move]', event) | |
} | |
} | |
}) | |
const pointerDownEventHandlerForRangeDot = event => { | |
if (getIsAnimatingRef() !== true) { | |
startAnimation() | |
} | |
} | |
return ( | |
<div> | |
<div> | |
{ getIsAnimatingState() ? 'Animating' : 'Stopped' } | |
</div> | |
<div style={containerStyle} className={`range ${containerClassName}`}> | |
<div className='range-dot' style={{ background: 'red' }} | |
> | |
=> stopAnimation()} | |
=> stopAnimation()} | |
/> | |
</div> | |
</div> | |
) | |
} |
This file contains 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
/* globals requestAnimationFrame, cancelAnimationFrame */ | |
import { useRef, useEffect, useState } from 'react' | |
export const useAnimationFrame = (onFrameTickCallback, startPaused = false) => { | |
const requestAnimationFrameReference = useRef() | |
const previousTimeReference = useRef() | |
const animationStateReference = useRef() | |
const [ isAnimating, setIsAnimating ] = useState(false) | |
const animate = timestamp => { | |
if (previousTimeReference.current !== undefined) { | |
const deltaTime = timestamp - previousTimeReference.current | |
animationStateReference.current = true | |
setIsAnimating(true) | |
onFrameTickCallback(timestamp, deltaTime) | |
} | |
previousTimeReference.current = timestamp | |
requestAnimationFrameReference.current = requestAnimationFrame(animate) | |
} | |
const stopAnimation = () => { | |
animationStateReference.current = false | |
setIsAnimating(false) | |
previousTimeReference.current = undefined | |
cancelAnimationFrame(requestAnimationFrameReference.current) | |
} | |
const startAnimation = () => { | |
requestAnimationFrameReference.current = requestAnimationFrame(animate) | |
} | |
const getIsAnimatingState = () => { | |
return isAnimating | |
} | |
const getIsAnimatingRef = () => { | |
return !!animationStateReference.current | |
} | |
useEffect(() => { | |
if (startPaused === false) { | |
startAnimation() | |
} | |
return () => stopAnimation() | |
}, []) | |
return [startAnimation, stopAnimation, getIsAnimatingRef, getIsAnimatingState] | |
} |
This file contains 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
import { useEffect } from 'react' | |
export const useDocumentEvents = (eventMapObject) => { | |
const rawEvents = Object.keys(eventMapObject).map((eventName) => { | |
const callback = eventMapObject[eventName] | |
return { | |
eventName, | |
callback | |
} | |
}) | |
useEffect(() => { | |
rawEvents.map(({ eventName, callback }) => { | |
document.addEventListener(eventName, callback) | |
}) | |
return () => { | |
rawEvents.map(({ eventName, callback }) => { | |
document.removeEventListener(eventName, callback) | |
}) | |
} | |
}, []) | |
} |
This file contains 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
import { useEffect } from 'react' | |
export const useElementEvents = (element, eventMapObject) => { | |
const rawEvents = Object.keys(eventMapObject).map((eventName) => { | |
const callback = eventMapObject[eventName] | |
return { | |
eventName, | |
callback | |
} | |
}) | |
useEffect(() => { | |
rawEvents.map(({ eventName, callback }) => { | |
element.addEventListener(eventName, callback) | |
}) | |
return () => { | |
rawEvents.map(({ eventName, callback }) => { | |
element.removeEventListener(eventName, callback) | |
}) | |
} | |
}, []) | |
} |
This file contains 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
import { useEffect } from 'react' | |
export const useGamepads = () => { | |
const connectSubscribers = new Set() | |
const disconnectSubscribers = new Set() | |
const handleGamepadConnected = event => { | |
for (const subscriber of connectSubscribers.values()) { | |
subscriber(event) | |
} | |
} | |
const handleGamepadDisconnected = event => { | |
for (const subscriber of disconnectSubscribers.values()) { | |
subscriber(event) | |
} | |
} | |
useEffect(() => { | |
window.addEventListener('gamepadconnected', handleGamepadConnected) | |
window.addEventListener('gamepaddisconnected', handleGamepadDisconnected) | |
return () => { | |
connectSubscribers.clear() | |
disconnectSubscribers.clear() | |
window.removeEventListener('gamepadconnected', handleGamepadConnected) | |
window.removeEventListener('gamepaddisconnected', handleGamepadDisconnected) | |
} | |
}, []) | |
return { | |
onGamepadConnected (callback) { | |
if (connectSubscribers.has(callback) === false) { | |
connectSubscribers.add(callback) | |
} | |
return () => { | |
if (connectSubscribers.has(callback)) { | |
connectSubscribers.delete(callback) | |
} | |
} | |
}, | |
onGamepadDisconnected (callback) { | |
if (disconnectSubscribers.has(callback) === false) { | |
disconnectSubscribers.add(callback) | |
} | |
return () => { | |
if (disconnectSubscribers.has(callback)) { | |
disconnectSubscribers.delete(callback) | |
} | |
} | |
}, | |
getGamepads () { | |
return navigator.getGamepads() | |
}, | |
makeButtonLogicTestMaker (gamepadInstance) { | |
return ({ testFn, callback }) => { | |
if (testFn(gamepadInstance)) { | |
callback() | |
} | |
} | |
} | |
} | |
} |
This file contains 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
// a simplified version. | |
/* globals requestAnimationFrame, cancelAnimationFrame */ | |
import { useEffect, useRef } from 'react' | |
export const useRequestAnimationFrame = renderCallback => { | |
const requestFrameRef = useRef() | |
const previousTimeRef = useRef() | |
const isFirstRenderRef = useRef() | |
const animate = timestamp => { | |
if (previousTimeRef.current !== undefined) { | |
const deltaTime = timestamp - previousTimeRef.current | |
const isFirstRender = isFirstRenderRef.current === undefined | |
isFirstRenderRef.current = false | |
renderCallback({ deltaTime, timestamp, isFirstRender }) | |
} | |
previousTimeRef.current = timestamp | |
requestFrameRef.current = requestAnimationFrame(animate) | |
} | |
useEffect(() => { | |
requestFrameRef.current = requestAnimationFrame(animate) | |
return () => cancelAnimationFrame(requestFrameRef.current) | |
}, []) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment