Skip to content

Instantly share code, notes, and snippets.

@finom
Last active June 29, 2020 18:32
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 finom/1d0da52add2db4c50af43eed6add4d92 to your computer and use it in GitHub Desktop.
Save finom/1d0da52add2db4c50af43eed6add4d92 to your computer and use it in GitHub Desktop.
[finom/use-array-state] A hook for creating observable-like immutable arrays based on useState
/*
A hook for creating observable-like immutable arrays based on useState
in other words allows to call push, pull, slice... methods which are going to deliver
a new array every time and trigger component to re-render
Usage:
const [array, arrayRef] = useArrayState(initialValue = []);
arrayRef.push(foo); // triggers component to re-render and makes 'array' to update
Besides built-in methods there is 'remove' method which removes items by value
arrayRef.remove(foo, bar); // removes foo and bar from array
and also 'set' method which overrides the array
arrayRef.set([1, 2, 3]);
besides array reference shown in the example it's available at arrayRef.value
*/
import {
useState, useRef, useEffect,
} from 'react';
import { without } from 'lodash';
const modyfyingMethodNames = 'push pop unshift shift sort reverse splice copyWithin fill'.split(/\s+/);
const allMethodNames = without(Object.getOwnPropertyNames(Array.prototype), 'constructor', 'length');
export default function useArrayState(initialValue = []) {
const [value, onSetValue] = useState(initialValue);
const { current: arrayRef } = useRef({ value });
useEffect(() => {
arrayRef.value = value;
}, [arrayRef, value]);
useEffect(() => {
allMethodNames.forEach((methodName) => {
if (modyfyingMethodNames.includes(methodName)) {
arrayRef[methodName] = (...args) => {
const newValue = [...arrayRef.value];
const result = Array.prototype[methodName].apply(newValue, args);
onSetValue(newValue);
return result;
};
} else {
arrayRef[methodName] = (...args) => Array.prototype[methodName].apply(arrayRef.value, args);
}
});
arrayRef.set = onSetValue;
arrayRef.remove = (...args) => {
onSetValue(without(arrayRef.value, ...args));
};
}, [arrayRef]);
return [value, arrayRef];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment