Skip to content

Instantly share code, notes, and snippets.

@treyhoover
Last active July 26, 2023 19:19
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save treyhoover/57c206da965b1ab5eb79369b4f4892cd to your computer and use it in GitHub Desktop.
Save treyhoover/57c206da965b1ab5eb79369b4f4892cd to your computer and use it in GitHub Desktop.
Hook for automatically creating helpers to
import { useState, useMemo } from "react";
// Usage example (booleans)
const userActivity = useStatus([true, false]);
userActivity.status; // true
userActivity.toggle();
userActivity.setTrue();
userActivity.setFalse();
userActivity.reset();
// Usage example (strings)
const camera = useStatus(["closed", "open"]);
camera.setClosed();
console.log(camera.isClosed); // true
camera.setOpen();
console.log(camera.isOpen); // true
camera.setClosed();
camera.setOpen();
camera.toggle();
type StatesUnion<T extends string[]> = T[number];
type ActionMap<T extends string[]> = {
[K in StatesUnion<T> as `set${Capitalize<K>}`]: () => void;
} & {
[K in StatesUnion<T> as `is${Capitalize<K>}`]: boolean;
} & {
status: StatesUnion<T>;
toggle: () => void;
reset: () => void;
};
export function useStatus<T extends string[]>(states: [...T]): ActionMap<T> {
const defaultStatus = states[0];
const [status, setStatus] = useState(defaultStatus);
const actionCreators: { [k: string]: () => void } = useMemo(() => {
return states.reduce((prev, cur) => {
const actionName = `set${cur.charAt(0).toUpperCase() + cur.slice(1)}`;
return { ...prev, [actionName]: () => setStatus(cur) };
}, {});
}, [states]);
const statusCheckers: { [k: string]: boolean } = useMemo(() => {
return states.reduce((prev, cur) => {
const checkerName = `is${cur.charAt(0).toUpperCase() + cur.slice(1)}`;
return { ...prev, [checkerName]: status === cur };
}, {});
}, [states, status]);
const toggle = () => {
setStatus((prevState) => {
const currentIndex = states.indexOf(prevState);
const nextIndex = (currentIndex + 1) % states.length;
return states[nextIndex];
});
};
const reset = () => {
setStatus(defaultStatus);
};
return {
status,
toggle,
reset,
...actionCreators,
...statusCheckers,
} as ActionMap<T>;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment