Last active
June 29, 2020 18:32
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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