@victorzimnikov/utility-hooks
v0.6.2
Published
Collection of low-level React hooks.
Downloads
4
Readme
utility-hooks
Collection of low-level React hooks.
Installation
npm install @victorzimnikov/utility-hooks
Environment compatibility
utility-hooks
output uses modern browser features, all extra transpilations and polyfills should be done in application side.
Static checking with react-hooks/exhaustive-deps
{
- "react-hooks/exhaustive-deps": ["warn"]
+ "react-hooks/exhaustive-deps": [
+ "warn",
+ {
+ "additionalHooks": "^(useMemoWith|usePromise|usePureDeps|usePureMemo|useIsomorphicLayoutEffect)$"
+ }
+ ]
}
Hooks
useEventCallback(callback)
Inspired by How to read an often-changing value from useCallback?
Unlike useCallback
, useEventCallback
does not accept second argument and stores original callback
in ref.
function Form() {
const [text, updateText] = useState("");
- const textRef = useRef();
-
- useEffect(() => {
- textRef.current = text; // Write it to the ref
- });
-
- const handleSubmit = useCallback(() => {
- const currentText = textRef.current; // Read it from the ref
- alert(currentText);
- }, [textRef]); // Don't recreate handleSubmit like [text] would do
+ const handleSubmit = useEventCallback(() => {
+ alert(text);
+ });
return (
<>
<input value={text} onChange={e => updateText(e.target.value)} />
<ExpensiveTree onSubmit={handleSubmit} />
</>
);
}
useIsomorphicLayoutEffect(effect, deps)
Inspired by react-redux/src/utils/useIsomorphicLayoutEffect
Runs useLayoutEffect
in browser environment (checks document.createElement
), otherwise useEffect
.
useConstant(factory)`
Inspired by How to create expensive objects lazily?
Runs factory only once and writes value in component ref
.
function Image(props) {
- const ref = useRef(null);
const node = useRef();
-
- // ✅ IntersectionObserver is created lazily once
- function getObserver() {
- let observer = ref.current;
- if (observer !== null) {
- return observer;
- }
- let newObserver = new IntersectionObserver(onIntersect);
- ref.current = newObserver;
- return newObserver;
- }
+ const observer = useConstant(() => new IntersectionObserver(onIntersect));
useEffect(() => {
- getObserver().observe(node.current);
+ observer.observe(node.current);
}, [observer]);
}
useMemoWith(factory, deps, isEqual)
Inspired by Gist.
Compares each dependency with isEqual
function to memoize value from factory
.
export function useFetch(url, options) {
- const cachedOptionsRef = useRef();
-
- if (
- !cachedOptionsRef.current ||
- !_.isEqual(options, cachedOptionsRef.current)
- ) {
- cachedOptionsRef.current = options;
- }
+ const cachedOptions = useMemoWith(() => options, [options], _.isEqual);
useEffect(() => {
// Perform fetch
- }, [url, cachedOptionsRef.current]);
+ }, [url, cachedOptions]);
}
usePrevious(value)
Inspired by How to get the previous props or state?
Stores value
used in previous render.
function Counter() {
- const prevCountRef = useRef();
const [count, setCount] = useState(0);
-
- useEffect(() => {
- prevCountRef.current = count;
- });
+ const prevCount = usePrevious(count);
return (
<h1>
- Now: {count}, before: {prevCountRef.current}
+ Now: {count}, before: {prevCount}
</h1>
);
}
usePromise(factory, deps)
Handles loading of promises created by factory
function.
const [filter, setFilter] = useState('')
- const [value, setValue] = useState();
- const [error, setError] = useState()
- useEffect(() => {
- const controller = new AbortController();
- const runEffect = async () => {
- try {
- const value = await fetch(
- "https://foo.bars/api?filter=" + filter,
- { signal: controller.signal }
- );
-
- setValue(value);
- } catch (error) {
- if (err.name === 'AbortError') {
- console.log("Request was canceled via controller.abort");
- return;
- }
-
- setError(error)
- }
- };
- runEffect();
- return () => controller.abort()
- }, [filter]);
+ const { value, error } = usePromise(({ abortSignal }) => fetch(
+ "https://foo.bars/api?filter=" + filter,
+ { signal: abortSignal }
+ ), [filter])
usePureMemo(deps, isEqual)
Returns next deps
only when they were changed based on isEqual
result.
usePureMemo(factory, deps, isEqual)
Works like useMemoWith
, but also compares return value.
export function useFetch(url, options) {
- const cachedOptionsRef = useRef();
-
- if (
- !cachedOptionsRef.current ||
- !_.isEqual(options, cachedOptionsRef.current)
- ) {
- cachedOptionsRef.current = options;
- }
+ const cachedOptions = usePureMemo(() => options, [options], _.isEqual);
useEffect(() => {
// Perform fetch
- }, [url, cachedOptionsRef.current]);
+ }, [url, cachedOptions]);
}
useValueRef(value)
Inspired by How to read an often-changing value from useCallback?
Works like useRef
, but keeps it's ref
in sync with value
on every call.
function Form() {
const [text, updateText] = useState('');
+ const textRef = useValueRef(text);
- const textRef = useRef();
-
- useEffect(() => {
- textRef.current = text; // Write it to the ref
- });
const handleSubmit = useCallback(() => {
const currentText = textRef.current; // Read it from the ref
alert(currentText);
}, [textRef]); // Don't recreate handleSubmit like [text] would do
return (
<>
<input value={text} onChange={e => updateText(e.target.value)} />
<ExpensiveTree onSubmit={handleSubmit} />
</>
);
}
useWhenValueChanges(value, effect, isEqual)
Works like useEffect
, but runs effect only when value
compared by isEqual
(Object.is
if not provided). It also passes the previous value
as an effect argument.
function List({ disptach, page, selectedId }) {
- const isInitial = useRef(true);
useEffect(() => {
- isInitial.current = true;
dispatch({ type: "FETCH_LIST", page });
}, [page, dispatch]);
useEffect(() => {
dispatch({ type: "FETCH_ITEM", id: selectedId });
}, [selectedId, dispatch]);
- useEffect(() => {
- if (isInitial.current) {
- isInitial.current = false;
- } else if (!selectedId) {
- dispatch({ type: "FETCH_LIST", page });
- }
- }, [page, selectedId, dispatch]);
+ useWhenValueChanges(selectedId, () => {
+ if (!selectedId) {
+ dispatch({ type: "FETCH_LIST", page });
+ }
+ });
}
Utilities
areDepsEqualWith(hookName, nextDeps, prevDeps, isEqual)
Compares each dependency with isEqual
function.