Skip to content

Instantly share code, notes, and snippets.

@Twipped
Last active January 6, 2021 02:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Twipped/f9dd76cda895b6244b1bcc38b9b3f17e to your computer and use it in GitHub Desktop.
Save Twipped/f9dd76cda895b6244b1bcc38b9b3f17e to your computer and use it in GitHub Desktop.
A small collection of useful react hooks
import {
isSameDay,
isSameYear,
isSameHour,
isSameMinute,
isSameMonth,
isSameSecond,
isSameWeek,
} from 'date-fns';
import { useState, useEffect, useRef } from 'react';
export const YEARS = isSameYear;
export const MONTHS = isSameMonth;
export const WEEKS = isSameWeek;
export const DAYS = isSameDay;
export const HOURS = isSameHour;
export const MINUTES = isSameMinute;
export const SECONDS = isSameSecond;
export default function useClock (...checks) {
checks = checks.flat(Infinity).filter((c) => typeof c === 'function');
if (!checks.length) throw new Error('You must provide tick functions to check against.');
const timerID = useRef(null);
const [ date, setDate ] = useState(new Date());
function tick () {
const now = new Date();
for (const check of checks) {
if (!check(now, date)) {
setDate(now);
return;
}
}
}
useEffect(() => {
timerID.current = setInterval( () => tick(), 1000 );
return () => { clearInterval(timerID.current); };
}, []);
return date;
}
useClock.YEARS = YEARS;
useClock.MONTHS = MONTHS;
useClock.WEEKS = WEEKS;
useClock.DAYS = DAYS;
useClock.HOURS = HOURS;
useClock.MINUTES = MINUTES;
useClock.SECONDS = SECONDS;
import useImmediateUpdateEffect from './useImmediateUpdateEffect';
import { useMemo, useCallback, useState } from 'react';
/**
* Creates a state hook populated by a value derived from dependencies.
* If the dependencies change, the state will be repopulated based on the new dependencies, IF the results differ.
* @param {Function} fn Handler to run at initialization and when a dependency changes
* @param {Array<mixed>} deps A dependency array
* @param {Function} comparator A function to evaluate if the result of the handler differs from current state
*/
const strictEqual = (a, b) => (a === b);
export default function useDerivedState (fn, deps, comparator = strictEqual) {
if (typeof fn === 'function') {
fn = useCallback(fn, deps);
} else {
const v = fn;
fn = useCallback(() => v, [ v ]);
}
const initial = useMemo(fn, deps);
var [ state, writeState ] = useState(initial);
useImmediateUpdateEffect(() => {
const res = fn();
if (comparator(state, res)) writeState(res);
}, deps);
return [ state, writeState ];
}
import useWillUnmount from './useWillUnmount';
import { useRef, useMemo } from 'react';
/**
* An _immediate_ effect that runs an effect callback when its dependency array
* changes. This is helpful for updates should must run during render, most
* commonly state derived from props; a more ergonomic version of https://reactjs.org/docs/hooks-faq.html#how-do-i-implement-getderivedstatefromprops
*
* ```ts
* function Example({ value }) {
* const [intermediaryValue, setValue] = useState(value);
*
* useImmediateUpdateEffect(() => {
* setValue(value)
* }, [value])
* ```
*/
export default function useImmediateUpdateEffect (effect, deps) {
const firstRef = useRef(true);
const tearDown = useRef();
useWillUnmount(() => {
if (tearDown.current) tearDown.current();
});
useMemo(() => {
if (firstRef.current) {
firstRef.current = false;
return;
}
if (tearDown.current) tearDown.current();
tearDown.current = effect();
}, deps);
}
import { useEffect, useRef } from 'react';
/**
* Attach a callback that fires when a component unmounts
*
* @param fn Handler to run when the component unmounts
* @category effects
*/
export default function useWillUnmount (fn) {
const onUnmount = useRef(fn);
onUnmount.current = fn;
useEffect(() => () => onUnmount.current(), []);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment