UNPKG

react-exo-hooks

Version:

A collection of useful hooks for data structures and logic, designed for efficiency

109 lines (108 loc) 3.25 kB
import { useState, useEffect } from 'react'; /** * This is a set that causes rerenders on updates * @note Effects and memos that use this set should also listen for its signal: `+INSTANCE` */ export class StatefulSet extends Set { /** The dispatch function for the signal */ _dispatchSignal; /** THe dispatch function for redefining the set */ _dispatchRedefine; /** * Construct a StatefulSet * @param initial The initial value (parameter for a vanilla set) * @param dispatchSignal The dispatch function for the signal */ constructor(initial, dispatchSignal) { super(initial); this._dispatchSignal = dispatchSignal; } /** * Set the redefine dispatch * @private * @param callback The function */ _setRedefine(callback) { this._dispatchRedefine = callback; } /** * Force a signal update */ forceUpdate() { this._dispatchSignal?.(-Math.random()); } /** * Set the instance to an entirely new instance * @param value The new instance * @returns The new instance * @throws {Error} If there's no redefinition callback defined */ reset(value) { if (!this._dispatchRedefine) throw new Error('Cannot redefine Set. No redefine callback set.'); const instance = new StatefulSet(value, this._dispatchSignal); this._dispatchRedefine(instance); instance._dispatchSignal?.(instance.size); return instance; } /** * @override */ add(value) { super.add(value); this._dispatchSignal?.(super.size); return this; } /** * @override */ delete(value) { const returnValue = super.delete(value); this._dispatchSignal?.(super.size); return returnValue; } /** * @override */ clear() { super.clear(); this._dispatchSignal?.(super.size); } /** * Toggle if an element is present within the set * @note This is a custom set method * @param value The value to toggle * @returns The new state: true if the value is now in the set, false if the value is now not in the set */ toggle(value) { if (super.has(value)) { super.delete(value); this._dispatchSignal?.(super.size); return false; } else { super.add(value); this._dispatchSignal?.(super.size); return false; } } /** * Returns the set's signal. Used for effects and memos that use this set * @returns A numeric signal */ valueOf() { return this.size; } } /** * Use a stately set * @note Any effects or memos that use this set should also listen for its signal (`+INSTANCE`) * @param initial The initial set value * @returns The stately set */ export function useSet(initial) { const [, setSignal] = useState(Array.isArray(initial) ? initial.length : initial instanceof Set ? initial.size : 0); const [set, setSet] = useState(new StatefulSet(initial, setSignal)); useEffect(() => set._setRedefine(setSet), [set]); return set; }