nucleux
Version:
Simple, atomic hub for all your React application's state management needs. No providers, no boilerplate, just state that works.
104 lines (103 loc) • 3.5 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Atom = void 0;
const es6_1 = __importDefault(require("fast-deep-equal/es6"));
const nanoid_1 = require("nanoid");
class Atom {
_value;
_initialValue;
subscribers = new Map();
persistence;
memoization;
constructor(initialValue, options) {
this._value = initialValue;
this._initialValue = initialValue;
this.persistence = options?.persistence;
this.memoization = options?.memoization;
this.subscribe = this.subscribe.bind(this);
this.unsubscribe = this.unsubscribe.bind(this);
this.hydrate();
}
async hydrate() {
if (this.persistence) {
const { persistKey, storage = localStorage } = this.persistence;
const rawPersistedValue = await storage.getItem(persistKey);
if (rawPersistedValue) {
try {
const persistedValue = JSON.parse(rawPersistedValue);
this.value = persistedValue;
}
catch (error) {
console.error(`Could not parse value ${rawPersistedValue} for ${persistKey}. Error:`, error);
}
}
else {
// fire-and-forget
storage.setItem(persistKey, JSON.stringify(this.value));
}
}
}
shouldSkipUpdate(newValue) {
const { type, compare } = this.memoization || { type: 'shallow' };
if (type === 'deep') {
return (0, es6_1.default)(newValue, this.value);
}
if (type === 'custom') {
return compare ? compare(newValue, this.value) : newValue === this.value;
}
return newValue === this.value;
}
notifySubscribers(newValue, previousValue) {
for (const [, subscriber] of this.subscribers) {
try {
if (subscriber.callback.length === 2) {
subscriber.callback(newValue, previousValue);
}
else {
subscriber.callback(newValue);
}
}
catch (error) {
console.error('Error in atom subscriber:', error);
}
}
}
get initialValue() {
return this._initialValue;
}
get value() {
return this._value;
}
set value(newValue) {
if (this.shouldSkipUpdate(newValue)) {
return;
}
const previousValue = this._value;
this._value = newValue;
if (newValue !== undefined && this.persistence) {
const { persistKey, storage = localStorage } = this.persistence;
// fire-and-forget
storage.setItem(persistKey, JSON.stringify(newValue));
}
this.notifySubscribers(newValue, previousValue);
}
subscribe(callback, immediate = false) {
const subId = (0, nanoid_1.nanoid)();
this.subscribers.set(subId, { callback });
if (immediate) {
callback(this.value);
}
return subId;
}
unsubscribe(subId) {
if (!this.subscribers.has(subId)) {
console.warn(`Subscriber ${subId} not found`);
return false;
}
return this.subscribers.delete(subId);
}
}
exports.Atom = Atom;