Skip to content

Instantly share code, notes, and snippets.

@Tylerian
Created August 10, 2020 10:49
Show Gist options
  • Save Tylerian/cfd755d3c52c0563e9cab9099d2c9d9a to your computer and use it in GitHub Desktop.
Save Tylerian/cfd755d3c52c0563e9cab9099d2c9d9a to your computer and use it in GitHub Desktop.
React useTransition hook
import {
useRef,
useEffect
} from "react";
function usePrevious(value) {
// The ref object is a generic container whose current property is mutable ...
// ... and can hold any value, similar to an instance property on a class
const ref = useRef(); // Store current value in ref
useEffect(() => {
ref.current = value;
}, [value]); // Only re-run if value changes
// Return previous value (happens before update in useEffect above)
return ref.current;
}
export {
usePrevious
};
import {
useState,
useCallback,
useEffect
} from "react";
import {
usePrevious
} from "./use-previous";
type UseTransitionConfig = {
in: boolean,
timeout: number,
onExited?: () => void,
onEntering?: () => void,
onEntered?: () => void,
onExiting?: () => void
};
const enum TransitionState {
EXITED = 'exited' ,
ENTERING = 'entering',
ENTERED = 'entered',
EXITING = 'exiting'
}
function useTransition(config: UseTransitionConfig): [TransitionState, boolean, boolean] {
const {
in: on,
timeout,
onExited,
onEntering,
onEntered,
onExiting
} = config;
const [
state,
setState
] = useState(TransitionState.EXITED);
const [
mounted,
setMounted
] = useState(false);
const [
transitioning,
setTransitioning
] = useState(false);
const prevOn = usePrevious(on);
const handleEntered = useCallback(() => {
setState(TransitionState.ENTERED);
if (onEntered) {
onEntered();
}
setTransitioning(false);
}, [onEntered]);
const handleEntering = useCallback(() => {
if (transitioning) {
return false;
}
setTransitioning(true);
setState(TransitionState.ENTERING);
if (onEntering) {
onEntering();
}
let id = setTimeout(handleEntered, timeout);
return () => clearTimeout(id);
}, [timeout, transitioning, onEntering, handleEntered]);
const handleExited = useCallback(() => {
setState(TransitionState.EXITED);
if (onExited) {
onExited();
}
setTransitioning(false);
setMounted(false);
}, [onExited]);
const handleExiting = useCallback(() => {
setTransitioning(true);
setState(TransitionState.EXITING);
if (onExiting) {
onExiting();
}
let id = setTimeout(handleExited, timeout);
return () => clearTimeout(id);
}, [timeout, handleExited, onExiting]);
useEffect(() => {
if (on && !prevOn) {
setMounted(true);
requestAnimationFrame(() => {
requestAnimationFrame(handleEntering);
});
}
if (prevOn && !on) {
handleExiting();
}
}, [on, prevOn, timeout, transitioning, handleEntering, handleExiting]);
return [state, mounted, transitioning];
}
export {
TransitionState,
useTransition
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment