Skip to content

Instantly share code, notes, and snippets.

@webbower
Last active April 5, 2025 16:51
Show Gist options
  • Save webbower/b1a617ae247ebac4de2a6ec8c7d3ca32 to your computer and use it in GitHub Desktop.
Save webbower/b1a617ae247ebac4de2a6ec8c7d3ca32 to your computer and use it in GitHub Desktop.
Custom hooks
import { useState, useRef } from 'react';
// Can't use React State because setting the constant to a function will execute that function in `useState`
// const useConstant = value => useState(value)[0];
const useConstant = value => useRef(value).current;
const myConst = useConstant('foo');
import { useState } from "react";
import { useConstant } from "./useConstant.js";
/**
* @template [Value=null]
* @template {Record<string, (...args: any[]) => void>} [Api={}]
* @typedef {Object & Api} CustomStateHookApi
* @prop {(newState: Value) => void} set Change the state to {@link newState}
* @prop {() => void} reset Change the state to the initial value
*/
/**
* React state hook with custom state setting API
*
* @todo add example
* ```jsx
* ```
*
* @template Value
* @template {Record<string, (...args: any[]) => void>} [Api={}]
* @param {(setState: (newState: Value) => void) => Api} [customApi={}] A function to define the custom API. It receives the `setState` function as an argument.
* @param {Value} [initialState=null] The initial state
* @returns {[Value, CustomStateHookApi<Value, Api>]} A tuple of the current state value and the hook API
*/
export const useCustomState = (customApi = () => ({}), initialState = null) => {
const [state, setState] = useState(initialState);
const api = useConstant(
() => ({
set: setState,
/**
* @note This will be the first initialState because `useConstant()` for the API captures the
* first value in this function definition closure.
*/
reset: () => {
setState(Boolean(initialState));
},
...customApi(setState),
}),
true
);
return [state, api];
};
import { useEffect } from 'react';
export const useEffectOnMounted = cb => {
useEffect(cb, []);
};
import { useState, useRef } from 'react';
// WIP
const useFormState = (stateShape = {}) => {
const [formState, setFormState] = useState(stateShape);
const api = useRef({
setFieldValue(fieldName, fieldValue) {
if (!stateShape.hasOwnProperty(fieldName)) {
throw new Error('...');
}
setFormState({
...formState,
[fieldName]: fieldValue,
});
},
handleFieldChange(ev) {
setFieldValue(ev.target.name, ev.target.value);
},
handleFieldValueChange(newValue, ev) {
setFieldValue(newValue, ev.target.name);
},
});
const { setFieldValue, handleFieldChange } = api.current;
return [formState, setFieldValue, handleFieldChange];
};
import { useConstant } from "./useConstant";
import { useObjectState } from "./useObjectState";
// TODO Add support for form errors
export const useFormState = (defaultFormData = {}) => {
const [formData, objectApi] = useObjectState(defaultFormData);
const api = useConstant(
() => ({
...objectApi,
handleValueChange: (fieldValue, fieldName) => {
objectApi.setField(fieldName, fieldValue);
},
handleChange: (ev) => {
const { name, value } = ev.target;
objectApi.setField(name, value);
},
}),
true
);
return [formData, api];
};
import { useRef } from 'react';
const useScrollManagement = () => {
const scrollLockState = useRef({
scrollPosition: null,
});
const lockScroll = () => {
scrollLockState.current = {
scrollPosition: window.scrollY,
};
window.scrollY = 0;
document.body.style.setProperty('overflow', 'hidden');
};
const unlockScroll = () => {
document.body.style.removeProperty('overflow');
window.scrollY = scrollLockState.current.scrollPosition;
scrollLockState.current = {
scrollPosition: null,
};
};
return { lockScroll, unlockScroll };
};
export default useScrollManagement;
import { useState, useRef } from 'react';
const useToggleState = (initialState = false) => {
if (typeof initialState !== 'boolean') {
throw new TypeError(`useToggleState expects initialState to be boolean. ${initialState} given.`);
}
const [state, setState] = useState(initialState);
const api = useRef({
on: () => setState(true),
off: () => setState(false),
toggle: () => setState(current => !current),
});
return [state, api.current];
};
const [isVisible, { on: show, off: hide }] = useToggleState();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment