jotai
Version:
👻 Next gen state management that will spook you
425 lines (415 loc) • 15.1 kB
JavaScript
var react = require('react');
var jotai = require('jotai');
function _extends() {
_extends = Object.assign ? Object.assign.bind() : function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(o);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
}
function _arrayLikeToArray(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
return arr2;
}
function _createForOfIteratorHelperLoose(o, allowArrayLike) {
var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
if (it) return (it = it.call(o)).next.bind(it);
if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
if (it) o = it;
var i = 0;
return function () {
if (i >= o.length) return {
done: true
};
return {
done: false,
value: o[i++]
};
};
}
throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
var RESTORE_ATOMS = 'h';
var DEV_SUBSCRIBE_STATE = 'n';
var DEV_GET_MOUNTED_ATOMS = 'l';
var DEV_GET_ATOM_STATE = 'a';
var DEV_GET_MOUNTED = 'm';
var atomToPrintable$1 = function atomToPrintable(atom) {
return atom.debugLabel || atom.toString();
};
var stateToPrintable = function stateToPrintable(_ref) {
var store = _ref[0],
atoms = _ref[1];
return Object.fromEntries(atoms.flatMap(function (atom) {
var _store$DEV_GET_MOUNTE, _store$DEV_GET_ATOM_S;
var mounted = (_store$DEV_GET_MOUNTE = store[DEV_GET_MOUNTED]) == null ? void 0 : _store$DEV_GET_MOUNTE.call(store, atom);
if (!mounted) {
return [];
}
var dependents = mounted.t;
var atomState = ((_store$DEV_GET_ATOM_S = store[DEV_GET_ATOM_STATE]) == null ? void 0 : _store$DEV_GET_ATOM_S.call(store, atom)) || {};
return [[atomToPrintable$1(atom), _extends({}, 'e' in atomState && {
error: atomState.e
}, 'p' in atomState && {
promise: atomState.p
}, 'v' in atomState && {
value: atomState.v
}, {
dependents: Array.from(dependents).map(atomToPrintable$1)
})]];
}));
};
var useAtomsDebugValue = function useAtomsDebugValue(options) {
var _options$enabled;
var enabled = (_options$enabled = options == null ? void 0 : options.enabled) != null ? _options$enabled : process.env.NODE_ENV !== "production";
var ScopeContext = jotai.SECRET_INTERNAL_getScopeContext(options == null ? void 0 : options.scope);
var _useContext = react.useContext(ScopeContext),
store = _useContext.s;
var _useState = react.useState([]),
atoms = _useState[0],
setAtoms = _useState[1];
react.useEffect(function () {
var _store$DEV_SUBSCRIBE_;
if (!enabled) {
return;
}
var callback = function callback() {
var _store$DEV_GET_MOUNTE2;
setAtoms(Array.from(((_store$DEV_GET_MOUNTE2 = store[DEV_GET_MOUNTED_ATOMS]) == null ? void 0 : _store$DEV_GET_MOUNTE2.call(store)) || []));
};
var unsubscribe = (_store$DEV_SUBSCRIBE_ = store[DEV_SUBSCRIBE_STATE]) == null ? void 0 : _store$DEV_SUBSCRIBE_.call(store, callback);
callback();
return unsubscribe;
}, [enabled, store]);
react.useDebugValue([store, atoms], stateToPrintable);
};
function useAtomDevtools(anAtom, options, deprecatedScope) {
if (typeof options === 'string' || typeof deprecatedScope !== 'undefined') {
console.warn('DEPRECATED [useAtomDevtools] use DevtoolOptions');
options = {
name: options,
scope: deprecatedScope
};
}
var _ref = options || {},
enabled = _ref.enabled,
name = _ref.name,
scope = _ref.scope;
var extension;
try {
extension = (enabled != null ? enabled : process.env.NODE_ENV !== "production") && window.__REDUX_DEVTOOLS_EXTENSION__;
} catch (_unused) {}
if (!extension) {
if (process.env.NODE_ENV !== "production" && enabled) {
console.warn('Please install/enable Redux devtools extension');
}
}
var _useAtom = jotai.useAtom(anAtom, scope),
value = _useAtom[0],
setValue = _useAtom[1];
var lastValue = react.useRef(value);
var isTimeTraveling = react.useRef(false);
var devtools = react.useRef();
var atomName = name || anAtom.debugLabel || anAtom.toString();
react.useEffect(function () {
if (!extension) {
return;
}
var setValueIfWritable = function setValueIfWritable(value) {
if (typeof setValue === 'function') {
setValue(value);
return;
}
console.warn('[Warn] you cannot do write operations (Time-travelling, etc) in read-only atoms\n', anAtom);
};
devtools.current = extension.connect({
name: atomName
});
var unsubscribe = devtools.current.subscribe(function (message) {
var _message$payload3, _message$payload4;
if (message.type === 'ACTION' && message.payload) {
try {
setValueIfWritable(JSON.parse(message.payload));
} catch (e) {
console.error('please dispatch a serializable value that JSON.parse() support\n', e);
}
} else if (message.type === 'DISPATCH' && message.state) {
var _message$payload, _message$payload2;
if (((_message$payload = message.payload) == null ? void 0 : _message$payload.type) === 'JUMP_TO_ACTION' || ((_message$payload2 = message.payload) == null ? void 0 : _message$payload2.type) === 'JUMP_TO_STATE') {
isTimeTraveling.current = true;
setValueIfWritable(JSON.parse(message.state));
}
} else if (message.type === 'DISPATCH' && ((_message$payload3 = message.payload) == null ? void 0 : _message$payload3.type) === 'COMMIT') {
var _devtools$current;
(_devtools$current = devtools.current) == null ? void 0 : _devtools$current.init(lastValue.current);
} else if (message.type === 'DISPATCH' && ((_message$payload4 = message.payload) == null ? void 0 : _message$payload4.type) === 'IMPORT_STATE') {
var _message$payload$next;
var computedStates = ((_message$payload$next = message.payload.nextLiftedState) == null ? void 0 : _message$payload$next.computedStates) || [];
computedStates.forEach(function (_ref2, index) {
var state = _ref2.state;
if (index === 0) {
var _devtools$current2;
(_devtools$current2 = devtools.current) == null ? void 0 : _devtools$current2.init(state);
} else {
setValueIfWritable(state);
}
});
}
});
devtools.current.shouldInit = true;
return unsubscribe;
}, [anAtom, extension, atomName, setValue]);
react.useEffect(function () {
if (!devtools.current) {
return;
}
lastValue.current = value;
if (devtools.current.shouldInit) {
devtools.current.init(value);
devtools.current.shouldInit = false;
} else if (isTimeTraveling.current) {
isTimeTraveling.current = false;
} else {
devtools.current.send(atomName + " - " + new Date().toLocaleString(), value);
}
}, [anAtom, extension, atomName, value]);
}
var isEqualAtomsValues = function isEqualAtomsValues(left, right) {
return left.size === right.size && Array.from(left).every(function (_ref) {
var left = _ref[0],
v = _ref[1];
return Object.is(right.get(left), v);
});
};
var isEqualAtomsDependents = function isEqualAtomsDependents(left, right) {
return left.size === right.size && Array.from(left).every(function (_ref2) {
var a = _ref2[0],
dLeft = _ref2[1];
var dRight = right.get(a);
return dRight && dLeft.size === dRight.size && Array.from(dLeft).every(function (d) {
return dRight.has(d);
});
});
};
function useAtomsSnapshot(scope) {
var ScopeContext = jotai.SECRET_INTERNAL_getScopeContext(scope);
var scopeContainer = react.useContext(ScopeContext);
var store = scopeContainer.s;
var _useState = react.useState(function () {
return {
values: new Map(),
dependents: new Map()
};
}),
atomsSnapshot = _useState[0],
setAtomsSnapshot = _useState[1];
react.useEffect(function () {
if (!store[DEV_SUBSCRIBE_STATE]) return;
var prevValues = new Map();
var prevDependents = new Map();
var invalidatedAtoms = new Set();
var callback = function callback() {
var values = new Map();
var dependents = new Map();
var hasNewInvalidatedAtoms = false;
for (var _iterator = _createForOfIteratorHelperLoose(store[DEV_GET_MOUNTED_ATOMS]() || []), _step; !(_step = _iterator()).done;) {
var atom = _step.value;
var atomState = store[DEV_GET_ATOM_STATE](atom);
if (atomState) {
if (!atomState.y) {
if ('p' in atomState) {
return;
}
if (!invalidatedAtoms.has(atom)) {
invalidatedAtoms.add(atom);
hasNewInvalidatedAtoms = true;
}
}
if ('v' in atomState) {
values.set(atom, atomState.v);
}
}
var mounted = store[DEV_GET_MOUNTED](atom);
if (mounted) {
dependents.set(atom, mounted.t);
}
}
if (hasNewInvalidatedAtoms) {
return;
}
if (isEqualAtomsValues(prevValues, values) && isEqualAtomsDependents(prevDependents, dependents)) {
return;
}
prevValues = values;
prevDependents = dependents;
invalidatedAtoms.clear();
setAtomsSnapshot({
values: values,
dependents: dependents
});
};
var unsubscribe = store[DEV_SUBSCRIBE_STATE](callback);
callback();
return unsubscribe;
}, [store]);
return atomsSnapshot;
}
function useGotoAtomsSnapshot(scope) {
var ScopeContext = jotai.SECRET_INTERNAL_getScopeContext(scope);
var _useContext = react.useContext(ScopeContext),
store = _useContext.s,
versionedWrite = _useContext.w;
return react.useCallback(function (snapshot) {
if (!store[DEV_SUBSCRIBE_STATE]) return;
var restoreAtoms = function restoreAtoms(values) {
if (versionedWrite) {
versionedWrite(function (version) {
store[RESTORE_ATOMS](values, version);
});
} else {
store[RESTORE_ATOMS](values);
}
};
if (isIterable(snapshot)) {
if (process.env.NODE_ENV !== "production") {
console.warn('snapshot as iterable is deprecated. use an object instead.');
}
restoreAtoms(snapshot);
return;
}
restoreAtoms(snapshot.values);
}, [store, versionedWrite]);
}
var isIterable = function isIterable(item) {
return typeof item[Symbol.iterator] === 'function';
};
var atomToPrintable = function atomToPrintable(atom) {
return atom.debugLabel ? atom + ":" + atom.debugLabel : "" + atom;
};
var getDevtoolsState = function getDevtoolsState(atomsSnapshot) {
var values = {};
atomsSnapshot.values.forEach(function (v, atom) {
values[atomToPrintable(atom)] = v;
});
var dependents = {};
atomsSnapshot.dependents.forEach(function (d, atom) {
dependents[atomToPrintable(atom)] = Array.from(d).map(atomToPrintable);
});
return {
values: values,
dependents: dependents
};
};
function useAtomsDevtools(name, options) {
if (typeof options !== 'undefined' && typeof options !== 'object') {
console.warn('DEPRECATED [useAtomsDevtools] use DevtoolsOptions');
options = {
scope: options
};
}
var _ref = options || {},
enabled = _ref.enabled,
scope = _ref.scope;
var extension;
try {
extension = (enabled != null ? enabled : process.env.NODE_ENV !== "production") && window.__REDUX_DEVTOOLS_EXTENSION__;
} catch (_unused) {}
if (!extension) {
if (process.env.NODE_ENV !== "production" && enabled) {
console.warn('Please install/enable Redux devtools extension');
}
}
var atomsSnapshot = useAtomsSnapshot(scope);
var goToSnapshot = useGotoAtomsSnapshot(scope);
var isTimeTraveling = react.useRef(false);
var isRecording = react.useRef(true);
var devtools = react.useRef();
var snapshots = react.useRef([]);
react.useEffect(function () {
if (!extension) {
return;
}
var getSnapshotAt = function getSnapshotAt(index) {
if (index === void 0) {
index = snapshots.current.length - 1;
}
var snapshot = snapshots.current[index >= 0 ? index : 0];
if (!snapshot) {
throw new Error('snaphost index out of bounds');
}
return snapshot;
};
var connection = extension.connect({
name: name
});
var devtoolsUnsubscribe = connection.subscribe(function (message) {
var _message$payload;
switch (message.type) {
case 'DISPATCH':
switch ((_message$payload = message.payload) == null ? void 0 : _message$payload.type) {
case 'RESET':
break;
case 'COMMIT':
connection.init(getDevtoolsState(getSnapshotAt()));
snapshots.current = [];
break;
case 'JUMP_TO_ACTION':
case 'JUMP_TO_STATE':
isTimeTraveling.current = true;
goToSnapshot(getSnapshotAt(message.payload.actionId - 1));
break;
case 'PAUSE_RECORDING':
isRecording.current = !isRecording.current;
break;
}
}
});
devtools.current = connection;
devtools.current.shouldInit = true;
return function () {
extension.disconnect();
devtoolsUnsubscribe == null ? void 0 : devtoolsUnsubscribe();
};
}, [extension, goToSnapshot, name]);
react.useEffect(function () {
if (!devtools.current) {
return;
}
if (devtools.current.shouldInit) {
devtools.current.init(undefined);
devtools.current.shouldInit = false;
return;
}
if (isTimeTraveling.current) {
isTimeTraveling.current = false;
} else if (isRecording.current) {
snapshots.current.push(atomsSnapshot);
devtools.current.send({
type: "" + snapshots.current.length,
updatedAt: new Date().toLocaleString()
}, getDevtoolsState(atomsSnapshot));
}
}, [atomsSnapshot]);
}
exports.useAtomDevtools = useAtomDevtools;
exports.useAtomsDebugValue = useAtomsDebugValue;
exports.useAtomsDevtools = useAtomsDevtools;
exports.useAtomsSnapshot = useAtomsSnapshot;
exports.useGotoAtomsSnapshot = useGotoAtomsSnapshot;
;