use-start-view-transition
Version:
A set of hooks to use `document.startViewTransition` upon state/value changes
56 lines (55 loc) • 2.03 kB
JavaScript
import { useCallback, useEffect, useRef, useState } from "react";
//#region src/index.ts
const startViewTransition = async (fn) => {
if (typeof window === "undefined" || !("startViewTransition" in document)) {
fn();
return;
}
try {
return document.startViewTransition(fn).ready.catch((error) => {
if (error instanceof Error && (error.name === "AbortError" || error.name === "InvalidStateError")) return;
throw error;
});
} catch (error) {
if (error instanceof Error && (error.name === "AbortError" || error.name === "InvalidStateError")) {
fn();
return;
}
throw error;
}
};
const useStartViewTransitionWrap = ([value, _setState], options = {}) => {
const optionsRef = useRef(options);
const isTransitionReadyRef = useRef(false);
optionsRef.current = options;
useEffect(() => {
const frameId = window.requestAnimationFrame(() => {
isTransitionReadyRef.current = true;
});
return () => {
window.cancelAnimationFrame(frameId);
};
}, []);
return [value, useCallback(async (v) => {
const { skipTransition } = optionsRef.current;
const skipFn = typeof skipTransition === "function" ? skipTransition : (v) => skipTransition;
if (isTransitionReadyRef.current && !skipFn(value)) return startViewTransition(() => _setState(v));
_setState(v);
}, [_setState, value])];
};
const useStartViewTransitionValue = (_value, options = {}) => {
const valueRef = useRef(_value);
const [value, setValue] = useStartViewTransitionWrap(useState(_value), options);
const { skipTransition } = options;
const shouldSkip = (typeof skipTransition === "function" ? skipTransition : (v) => skipTransition)(_value);
if (_value !== valueRef.current) {
valueRef.current = _value;
setValue(_value);
}
return shouldSkip ? _value : value;
};
const useStartViewTransitionState = (state, options = {}) => {
return useStartViewTransitionWrap(useState(state), options);
};
//#endregion
export { startViewTransition, useStartViewTransitionState, useStartViewTransitionValue, useStartViewTransitionWrap };