Comparing version 0.6.3 to 0.7.0
@@ -10,3 +10,3 @@ /** | ||
*/ | ||
export declare type Store<T extends IState = Record<string, any>> = { | ||
export type Store<T extends IState = Record<string, any>> = { | ||
/** | ||
@@ -45,7 +45,7 @@ * Return the current state. | ||
}; | ||
export declare type StateUpdateFunction<T extends IState> = (state: Readonly<T>) => Partial<T>; | ||
export type StateUpdateFunction<T extends IState> = (state: Readonly<T>) => Partial<T>; | ||
/** | ||
* Options for the `set` function. | ||
*/ | ||
export declare type SetOptions = { | ||
export type SetOptions = { | ||
forceNotify?: boolean; | ||
@@ -56,3 +56,3 @@ }; | ||
*/ | ||
export declare type Listener<T extends IState> = (updates: Readonly<Partial<T>>, state: Readonly<T>) => void; | ||
export type Listener<T extends IState> = (updates: Readonly<Partial<T>>, state: Readonly<T>) => void; | ||
/** | ||
@@ -67,3 +67,3 @@ * Creates a Statery store and populates it with an initial state. | ||
* provides direct access to the store's state and makes sure that the React component | ||
* it was invoked from automaticaly re-renders when any of the data it uses is updated. | ||
* it was invoked from automatically re-renders when any of the data it uses is updated. | ||
* | ||
@@ -70,0 +70,0 @@ * @param store The Statery store to access. |
export * from "./declarations/src/index"; | ||
//# sourceMappingURL=statery.cjs.d.ts.map |
@@ -7,41 +7,5 @@ 'use strict'; | ||
function _arrayWithHoles(arr) { | ||
if (Array.isArray(arr)) return arr; | ||
} | ||
function _iterableToArrayLimit(arr, i) { | ||
var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; | ||
if (_i == null) return; | ||
var _arr = []; | ||
var _n = true; | ||
var _d = false; | ||
var _s, _e; | ||
try { | ||
for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { | ||
_arr.push(_s.value); | ||
if (i && _arr.length === i) break; | ||
} | ||
} catch (err) { | ||
_d = true; | ||
_e = err; | ||
} finally { | ||
try { | ||
if (!_n && _i["return"] != null) _i["return"](); | ||
} finally { | ||
if (_d) throw _e; | ||
} | ||
} | ||
return _arr; | ||
} | ||
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; | ||
@@ -59,13 +23,4 @@ } | ||
function _nonIterableRest() { | ||
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); | ||
} | ||
function _slicedToArray(arr, i) { | ||
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); | ||
} | ||
function _createForOfIteratorHelper(o, allowArrayLike) { | ||
var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; | ||
if (!it) { | ||
@@ -75,5 +30,3 @@ if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { | ||
var i = 0; | ||
var F = function () {}; | ||
return { | ||
@@ -96,9 +49,7 @@ s: F, | ||
} | ||
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 normalCompletion = true, | ||
didErr = false, | ||
err; | ||
didErr = false, | ||
err; | ||
return { | ||
@@ -127,3 +78,20 @@ s: function () { | ||
function _toPrimitive(input, hint) { | ||
if (typeof input !== "object" || input === null) return input; | ||
var prim = input[Symbol.toPrimitive]; | ||
if (prim !== undefined) { | ||
var res = prim.call(input, hint || "default"); | ||
if (typeof res !== "object") return res; | ||
throw new TypeError("@@toPrimitive must return a primitive value."); | ||
} | ||
return (hint === "string" ? String : Number)(input); | ||
} | ||
function _toPropertyKey(arg) { | ||
var key = _toPrimitive(arg, "string"); | ||
return typeof key === "symbol" ? key : String(key); | ||
} | ||
function _defineProperty(obj, key, value) { | ||
key = _toPropertyKey(key); | ||
if (key in obj) { | ||
@@ -139,3 +107,2 @@ Object.defineProperty(obj, key, { | ||
} | ||
return obj; | ||
@@ -146,3 +113,2 @@ } | ||
var keys = Object.keys(object); | ||
if (Object.getOwnPropertySymbols) { | ||
@@ -154,6 +120,4 @@ var symbols = Object.getOwnPropertySymbols(object); | ||
} | ||
return keys; | ||
} | ||
function _objectSpread2(target) { | ||
@@ -168,3 +132,2 @@ for (var i = 1; i < arguments.length; i++) { | ||
} | ||
return target; | ||
@@ -176,3 +139,2 @@ } | ||
var listeners = new Set(); | ||
var getActualChanges = function getActualChanges(updates) { | ||
@@ -184,3 +146,2 @@ return Object.keys(updates).reduce(function (changes, prop) { | ||
}; | ||
return { | ||
@@ -190,22 +151,16 @@ get state() { | ||
}, | ||
set: function set(incoming) { | ||
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
_ref$forceNotify = _ref.forceNotify, | ||
forceNotify = _ref$forceNotify === void 0 ? false : _ref$forceNotify; | ||
_ref$forceNotify = _ref.forceNotify, | ||
forceNotify = _ref$forceNotify === void 0 ? false : _ref$forceNotify; | ||
var incomingState = incoming instanceof Function ? incoming(state) : incoming; | ||
var updates = forceNotify ? incomingState : getActualChanges(incomingState); | ||
if (Object.keys(updates).length > 0) { | ||
var previousState = state; | ||
state = _objectSpread2(_objectSpread2({}, state), updates); | ||
var _iterator = _createForOfIteratorHelper(listeners), | ||
_step; | ||
_step; | ||
try { | ||
for (_iterator.s(); !(_step = _iterator.n()).done;) { | ||
var _listener = _step.value; | ||
_listener(updates, previousState); | ||
@@ -219,3 +174,2 @@ } | ||
} | ||
return state; | ||
@@ -231,50 +185,43 @@ }, | ||
}; | ||
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? react.useLayoutEffect : react.useEffect; | ||
var useStore = function useStore(store) { | ||
var _useState = react.useState(0), | ||
_useState2 = _slicedToArray(_useState, 2); | ||
_useState2[0]; | ||
var setVersion = _useState2[1]; | ||
var subscribedProps = useConst(function () { | ||
return new Set(); | ||
}); | ||
var initialState = useConst(function () { | ||
return store.state; | ||
}); | ||
useIsomorphicLayoutEffect(function () { | ||
if (store.state === initialState) return; | ||
subscribedProps.forEach(function (prop) { | ||
if (initialState[prop] !== store.state[prop]) { | ||
setVersion(function (v) { | ||
return v + 1; | ||
}); | ||
return; | ||
} | ||
}); | ||
}, [store]); | ||
useIsomorphicLayoutEffect(function () { | ||
var listener = function listener(updates) { | ||
if (Object.keys(updates).find(function (prop) { | ||
return subscribedProps.has(prop); | ||
})) { | ||
setVersion(function (v) { | ||
return v + 1; | ||
}); | ||
} | ||
}; | ||
var prevSnapshot = react.useRef(store.state); | ||
var subscribe = react.useCallback(function (listener) { | ||
store.subscribe(listener); | ||
return function () { | ||
return void store.unsubscribe(listener); | ||
return store.unsubscribe(listener); | ||
}; | ||
}, [store]); | ||
var getSnapshot = react.useCallback(function () { | ||
var hasChanged = false; | ||
var _iterator2 = _createForOfIteratorHelper(subscribedProps), | ||
_step2; | ||
try { | ||
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { | ||
var prop = _step2.value; | ||
if (store.state[prop] !== prevSnapshot.current[prop]) { | ||
hasChanged = true; | ||
break; | ||
} | ||
} | ||
} catch (err) { | ||
_iterator2.e(err); | ||
} finally { | ||
_iterator2.f(); | ||
} | ||
if (hasChanged) { | ||
prevSnapshot.current = store.state; | ||
} | ||
return prevSnapshot.current; | ||
}, [store]); | ||
var snapshot = react.useSyncExternalStore(subscribe, getSnapshot); | ||
return new Proxy({}, { | ||
get: function get(_, prop) { | ||
subscribedProps.add(prop); | ||
return store.state[prop]; | ||
return snapshot[prop]; | ||
} | ||
}); | ||
}; | ||
var useConst = function useConst(ctor) { | ||
@@ -281,0 +228,0 @@ var ref = react.useRef(null); |
@@ -7,41 +7,5 @@ 'use strict'; | ||
function _arrayWithHoles(arr) { | ||
if (Array.isArray(arr)) return arr; | ||
} | ||
function _iterableToArrayLimit(arr, i) { | ||
var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; | ||
if (_i == null) return; | ||
var _arr = []; | ||
var _n = true; | ||
var _d = false; | ||
var _s, _e; | ||
try { | ||
for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { | ||
_arr.push(_s.value); | ||
if (i && _arr.length === i) break; | ||
} | ||
} catch (err) { | ||
_d = true; | ||
_e = err; | ||
} finally { | ||
try { | ||
if (!_n && _i["return"] != null) _i["return"](); | ||
} finally { | ||
if (_d) throw _e; | ||
} | ||
} | ||
return _arr; | ||
} | ||
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; | ||
@@ -59,13 +23,4 @@ } | ||
function _nonIterableRest() { | ||
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); | ||
} | ||
function _slicedToArray(arr, i) { | ||
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); | ||
} | ||
function _createForOfIteratorHelper(o, allowArrayLike) { | ||
var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; | ||
if (!it) { | ||
@@ -75,5 +30,3 @@ if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { | ||
var i = 0; | ||
var F = function () {}; | ||
return { | ||
@@ -96,9 +49,7 @@ s: F, | ||
} | ||
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 normalCompletion = true, | ||
didErr = false, | ||
err; | ||
didErr = false, | ||
err; | ||
return { | ||
@@ -127,3 +78,20 @@ s: function () { | ||
function _toPrimitive(input, hint) { | ||
if (typeof input !== "object" || input === null) return input; | ||
var prim = input[Symbol.toPrimitive]; | ||
if (prim !== undefined) { | ||
var res = prim.call(input, hint || "default"); | ||
if (typeof res !== "object") return res; | ||
throw new TypeError("@@toPrimitive must return a primitive value."); | ||
} | ||
return (hint === "string" ? String : Number)(input); | ||
} | ||
function _toPropertyKey(arg) { | ||
var key = _toPrimitive(arg, "string"); | ||
return typeof key === "symbol" ? key : String(key); | ||
} | ||
function _defineProperty(obj, key, value) { | ||
key = _toPropertyKey(key); | ||
if (key in obj) { | ||
@@ -139,3 +107,2 @@ Object.defineProperty(obj, key, { | ||
} | ||
return obj; | ||
@@ -146,3 +113,2 @@ } | ||
var keys = Object.keys(object); | ||
if (Object.getOwnPropertySymbols) { | ||
@@ -154,6 +120,4 @@ var symbols = Object.getOwnPropertySymbols(object); | ||
} | ||
return keys; | ||
} | ||
function _objectSpread2(target) { | ||
@@ -168,3 +132,2 @@ for (var i = 1; i < arguments.length; i++) { | ||
} | ||
return target; | ||
@@ -176,3 +139,2 @@ } | ||
var listeners = new Set(); | ||
var getActualChanges = function getActualChanges(updates) { | ||
@@ -184,3 +146,2 @@ return Object.keys(updates).reduce(function (changes, prop) { | ||
}; | ||
return { | ||
@@ -190,22 +151,16 @@ get state() { | ||
}, | ||
set: function set(incoming) { | ||
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
_ref$forceNotify = _ref.forceNotify, | ||
forceNotify = _ref$forceNotify === void 0 ? false : _ref$forceNotify; | ||
_ref$forceNotify = _ref.forceNotify, | ||
forceNotify = _ref$forceNotify === void 0 ? false : _ref$forceNotify; | ||
var incomingState = incoming instanceof Function ? incoming(state) : incoming; | ||
var updates = forceNotify ? incomingState : getActualChanges(incomingState); | ||
if (Object.keys(updates).length > 0) { | ||
var previousState = state; | ||
state = _objectSpread2(_objectSpread2({}, state), updates); | ||
var _iterator = _createForOfIteratorHelper(listeners), | ||
_step; | ||
_step; | ||
try { | ||
for (_iterator.s(); !(_step = _iterator.n()).done;) { | ||
var _listener = _step.value; | ||
_listener(updates, previousState); | ||
@@ -219,3 +174,2 @@ } | ||
} | ||
return state; | ||
@@ -231,50 +185,43 @@ }, | ||
}; | ||
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? react.useLayoutEffect : react.useEffect; | ||
var useStore = function useStore(store) { | ||
var _useState = react.useState(0), | ||
_useState2 = _slicedToArray(_useState, 2); | ||
_useState2[0]; | ||
var setVersion = _useState2[1]; | ||
var subscribedProps = useConst(function () { | ||
return new Set(); | ||
}); | ||
var initialState = useConst(function () { | ||
return store.state; | ||
}); | ||
useIsomorphicLayoutEffect(function () { | ||
if (store.state === initialState) return; | ||
subscribedProps.forEach(function (prop) { | ||
if (initialState[prop] !== store.state[prop]) { | ||
setVersion(function (v) { | ||
return v + 1; | ||
}); | ||
return; | ||
} | ||
}); | ||
}, [store]); | ||
useIsomorphicLayoutEffect(function () { | ||
var listener = function listener(updates) { | ||
if (Object.keys(updates).find(function (prop) { | ||
return subscribedProps.has(prop); | ||
})) { | ||
setVersion(function (v) { | ||
return v + 1; | ||
}); | ||
} | ||
}; | ||
var prevSnapshot = react.useRef(store.state); | ||
var subscribe = react.useCallback(function (listener) { | ||
store.subscribe(listener); | ||
return function () { | ||
return void store.unsubscribe(listener); | ||
return store.unsubscribe(listener); | ||
}; | ||
}, [store]); | ||
var getSnapshot = react.useCallback(function () { | ||
var hasChanged = false; | ||
var _iterator2 = _createForOfIteratorHelper(subscribedProps), | ||
_step2; | ||
try { | ||
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { | ||
var prop = _step2.value; | ||
if (store.state[prop] !== prevSnapshot.current[prop]) { | ||
hasChanged = true; | ||
break; | ||
} | ||
} | ||
} catch (err) { | ||
_iterator2.e(err); | ||
} finally { | ||
_iterator2.f(); | ||
} | ||
if (hasChanged) { | ||
prevSnapshot.current = store.state; | ||
} | ||
return prevSnapshot.current; | ||
}, [store]); | ||
var snapshot = react.useSyncExternalStore(subscribe, getSnapshot); | ||
return new Proxy({}, { | ||
get: function get(_, prop) { | ||
subscribedProps.add(prop); | ||
return store.state[prop]; | ||
return snapshot[prop]; | ||
} | ||
}); | ||
}; | ||
var useConst = function useConst(ctor) { | ||
@@ -281,0 +228,0 @@ var ref = react.useRef(null); |
@@ -1,42 +0,6 @@ | ||
import { useState, useRef, useLayoutEffect, useEffect } from 'react'; | ||
import { useRef, useCallback, useSyncExternalStore } from 'react'; | ||
function _arrayWithHoles(arr) { | ||
if (Array.isArray(arr)) return arr; | ||
} | ||
function _iterableToArrayLimit(arr, i) { | ||
var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; | ||
if (_i == null) return; | ||
var _arr = []; | ||
var _n = true; | ||
var _d = false; | ||
var _s, _e; | ||
try { | ||
for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { | ||
_arr.push(_s.value); | ||
if (i && _arr.length === i) break; | ||
} | ||
} catch (err) { | ||
_d = true; | ||
_e = err; | ||
} finally { | ||
try { | ||
if (!_n && _i["return"] != null) _i["return"](); | ||
} finally { | ||
if (_d) throw _e; | ||
} | ||
} | ||
return _arr; | ||
} | ||
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; | ||
@@ -54,13 +18,4 @@ } | ||
function _nonIterableRest() { | ||
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); | ||
} | ||
function _slicedToArray(arr, i) { | ||
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); | ||
} | ||
function _createForOfIteratorHelper(o, allowArrayLike) { | ||
var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; | ||
if (!it) { | ||
@@ -70,5 +25,3 @@ if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { | ||
var i = 0; | ||
var F = function () {}; | ||
return { | ||
@@ -91,9 +44,7 @@ s: F, | ||
} | ||
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 normalCompletion = true, | ||
didErr = false, | ||
err; | ||
didErr = false, | ||
err; | ||
return { | ||
@@ -122,3 +73,20 @@ s: function () { | ||
function _toPrimitive(input, hint) { | ||
if (typeof input !== "object" || input === null) return input; | ||
var prim = input[Symbol.toPrimitive]; | ||
if (prim !== undefined) { | ||
var res = prim.call(input, hint || "default"); | ||
if (typeof res !== "object") return res; | ||
throw new TypeError("@@toPrimitive must return a primitive value."); | ||
} | ||
return (hint === "string" ? String : Number)(input); | ||
} | ||
function _toPropertyKey(arg) { | ||
var key = _toPrimitive(arg, "string"); | ||
return typeof key === "symbol" ? key : String(key); | ||
} | ||
function _defineProperty(obj, key, value) { | ||
key = _toPropertyKey(key); | ||
if (key in obj) { | ||
@@ -134,3 +102,2 @@ Object.defineProperty(obj, key, { | ||
} | ||
return obj; | ||
@@ -141,3 +108,2 @@ } | ||
var keys = Object.keys(object); | ||
if (Object.getOwnPropertySymbols) { | ||
@@ -149,6 +115,4 @@ var symbols = Object.getOwnPropertySymbols(object); | ||
} | ||
return keys; | ||
} | ||
function _objectSpread2(target) { | ||
@@ -163,3 +127,2 @@ for (var i = 1; i < arguments.length; i++) { | ||
} | ||
return target; | ||
@@ -171,3 +134,2 @@ } | ||
var listeners = new Set(); | ||
var getActualChanges = function getActualChanges(updates) { | ||
@@ -179,3 +141,2 @@ return Object.keys(updates).reduce(function (changes, prop) { | ||
}; | ||
return { | ||
@@ -185,22 +146,16 @@ get state() { | ||
}, | ||
set: function set(incoming) { | ||
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
_ref$forceNotify = _ref.forceNotify, | ||
forceNotify = _ref$forceNotify === void 0 ? false : _ref$forceNotify; | ||
_ref$forceNotify = _ref.forceNotify, | ||
forceNotify = _ref$forceNotify === void 0 ? false : _ref$forceNotify; | ||
var incomingState = incoming instanceof Function ? incoming(state) : incoming; | ||
var updates = forceNotify ? incomingState : getActualChanges(incomingState); | ||
if (Object.keys(updates).length > 0) { | ||
var previousState = state; | ||
state = _objectSpread2(_objectSpread2({}, state), updates); | ||
var _iterator = _createForOfIteratorHelper(listeners), | ||
_step; | ||
_step; | ||
try { | ||
for (_iterator.s(); !(_step = _iterator.n()).done;) { | ||
var _listener = _step.value; | ||
_listener(updates, previousState); | ||
@@ -214,3 +169,2 @@ } | ||
} | ||
return state; | ||
@@ -226,50 +180,43 @@ }, | ||
}; | ||
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect; | ||
var useStore = function useStore(store) { | ||
var _useState = useState(0), | ||
_useState2 = _slicedToArray(_useState, 2); | ||
_useState2[0]; | ||
var setVersion = _useState2[1]; | ||
var subscribedProps = useConst(function () { | ||
return new Set(); | ||
}); | ||
var initialState = useConst(function () { | ||
return store.state; | ||
}); | ||
useIsomorphicLayoutEffect(function () { | ||
if (store.state === initialState) return; | ||
subscribedProps.forEach(function (prop) { | ||
if (initialState[prop] !== store.state[prop]) { | ||
setVersion(function (v) { | ||
return v + 1; | ||
}); | ||
return; | ||
} | ||
}); | ||
}, [store]); | ||
useIsomorphicLayoutEffect(function () { | ||
var listener = function listener(updates) { | ||
if (Object.keys(updates).find(function (prop) { | ||
return subscribedProps.has(prop); | ||
})) { | ||
setVersion(function (v) { | ||
return v + 1; | ||
}); | ||
} | ||
}; | ||
var prevSnapshot = useRef(store.state); | ||
var subscribe = useCallback(function (listener) { | ||
store.subscribe(listener); | ||
return function () { | ||
return void store.unsubscribe(listener); | ||
return store.unsubscribe(listener); | ||
}; | ||
}, [store]); | ||
var getSnapshot = useCallback(function () { | ||
var hasChanged = false; | ||
var _iterator2 = _createForOfIteratorHelper(subscribedProps), | ||
_step2; | ||
try { | ||
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { | ||
var prop = _step2.value; | ||
if (store.state[prop] !== prevSnapshot.current[prop]) { | ||
hasChanged = true; | ||
break; | ||
} | ||
} | ||
} catch (err) { | ||
_iterator2.e(err); | ||
} finally { | ||
_iterator2.f(); | ||
} | ||
if (hasChanged) { | ||
prevSnapshot.current = store.state; | ||
} | ||
return prevSnapshot.current; | ||
}, [store]); | ||
var snapshot = useSyncExternalStore(subscribe, getSnapshot); | ||
return new Proxy({}, { | ||
get: function get(_, prop) { | ||
subscribedProps.add(prop); | ||
return store.state[prop]; | ||
return snapshot[prop]; | ||
} | ||
}); | ||
}; | ||
var useConst = function useConst(ctor) { | ||
@@ -276,0 +223,0 @@ var ref = useRef(null); |
@@ -1,2 +0,2 @@ | ||
Copyright (c) 2020 Hendrik Mans | ||
Copyright (c) 2023 Hendrik Mans | ||
@@ -3,0 +3,0 @@ Permission is hereby granted, free of charge, to any person obtaining |
@@ -6,3 +6,3 @@ { | ||
"email": "hendrik@mans.de", | ||
"url": "https://hendrik.mans.de" | ||
"url": "https://hmans.dev" | ||
}, | ||
@@ -18,6 +18,6 @@ "description": "A happy little state management library for React and friends.", | ||
"sideEffects": false, | ||
"version": "0.6.3", | ||
"version": "0.7.0", | ||
"main": "dist/statery.cjs.js", | ||
"module": "dist/statery.esm.js", | ||
"types": "dist/statery.cjs.js", | ||
"types": "dist/statery.cjs.d.ts", | ||
"files": [ | ||
@@ -34,31 +34,24 @@ "dist/", | ||
"@babel/preset-react", | ||
[ | ||
"@babel/preset-typescript", | ||
{ | ||
"isTSX": true, | ||
"allExtensions": true | ||
} | ||
] | ||
"@babel/preset-typescript" | ||
] | ||
}, | ||
"devDependencies": { | ||
"@babel/core": "^7.19.0", | ||
"@babel/preset-env": "^7.19.0", | ||
"@babel/preset-react": "^7.18.6", | ||
"@babel/preset-typescript": "^7.18.6", | ||
"@changesets/cli": "^2.24.1", | ||
"@preconstruct/cli": "^2.2.1", | ||
"@testing-library/react": "^13.3.0", | ||
"@types/jest": "^28.1.6", | ||
"@types/react": "^18.0.19", | ||
"@types/react-dom": "^18.0.0", | ||
"jest": "^28.1.3", | ||
"jest-environment-jsdom": "^28.1.3", | ||
"@babel/core": "^7.22.9", | ||
"@babel/preset-env": "^7.22.9", | ||
"@babel/preset-react": "^7.22.5", | ||
"@babel/preset-typescript": "^7.22.5", | ||
"@changesets/cli": "^2.26.2", | ||
"@preconstruct/cli": "^2.8.1", | ||
"@testing-library/react": "^14.0.0", | ||
"@types/jest": "^29.5.3", | ||
"@types/react": "^18.2.18", | ||
"@types/react-dom": "^18.2.7", | ||
"jest": "^29.6.2", | ||
"jest-environment-jsdom": "^29.6.2", | ||
"react": "^18.1.0", | ||
"react-dom": "^18.1.0", | ||
"rimraf": "^3.0.2", | ||
"ts-jest": "^28.0.7", | ||
"tslib": "^2.0.3", | ||
"typedoc": "^0.23.9", | ||
"typescript": "^4.1.3" | ||
"rimraf": "^5.0.1", | ||
"ts-jest": "^29.1.1", | ||
"tslib": "^2.6.1", | ||
"typescript": "^5.1.6" | ||
}, | ||
@@ -68,2 +61,7 @@ "peerDependencies": { | ||
}, | ||
"peerDependenciesMeta": { | ||
"react": { | ||
"optional": true | ||
} | ||
}, | ||
"scripts": { | ||
@@ -74,7 +72,6 @@ "clean": "rimraf dist", | ||
"test": "jest", | ||
"docs": "typedoc src/index.ts", | ||
"ci": "preconstruct validate && pnpm build && pnpm test", | ||
"ci:test": "preconstruct validate && pnpm build && pnpm test", | ||
"ci:version": "changeset version && pnpm install --no-frozen-lockfile", | ||
"ci:release": "pnpm ci && pnpm changeset publish" | ||
"ci:release": "pnpm ci:test && pnpm changeset publish" | ||
} | ||
} |
@@ -16,3 +16,3 @@ ``` | ||
[![Version](https://img.shields.io/npm/v/statery?style=for-the-badge)](https://www.npmjs.com/package/statery) | ||
[![CI](https://img.shields.io/github/workflow/status/hmans/statery/CI?style=for-the-badge)](https://github.com/hmans/statery/actions?query=workflow%3ACI) | ||
![CI](https://img.shields.io/github/actions/workflow/status/hmans/statery/tests.yml?style=for-the-badge) | ||
[![Downloads](https://img.shields.io/npm/dt/statery.svg?style=for-the-badge)](https://www.npmjs.com/package/statery) | ||
@@ -26,3 +26,3 @@ [![Bundle Size](https://img.shields.io/bundlephobia/min/statery?label=bundle%20size&style=for-the-badge)](https://bundlephobia.com/result?p=statery) | ||
- Fully **tested**, fully **typed**! | ||
- **Designed for React** (with functional components and hooks), but can also be used **without it**. | ||
- **Designed for React** (with functional components and hooks), but can also be used **without it**, or with other frameworks (but you may need to bring your own glue.) | ||
@@ -37,18 +37,6 @@ ### Non-Features ๐งค | ||
### Comparison to Zustand | ||
[Zustand](https://github.com/pmndrs/zustand) is a lovely minimal state management library for React, and Statery may feel very similar to it. Here are the key differences: | ||
- In Zustand, **data and the functions that modify it are all stored in the same object**. I believe this to be architecturally unwise (the README even warns the user not to accidentally overwrite their store's API.) Furthermore, this leads to Zustand [not being able to infer the type](https://github.com/pmndrs/zustand/blob/main/docs/guides/typescript.md) of the state data from the `create` function's first argument, forcing you to provide it explicitly (including the API, because it's part of the state... and so on.) | ||
Statery doesn't share these problems; **stores _only_ contain data**. The store's type is inferred from the initial state passed into `makeStore`. You can either modify the state directly through `set`, or โ the recommended approach โ export an API for your store as a set of functions (see the examples in this document or the [demo] for what this looks like.) | ||
- When accessing data using Zustand's `useStore` hook, **it expects you to provide a selector function** that returns the data you want. Statery's `useStore` instead uses a **transparent proxy** that automatically registers all state properties you acceess. **You don't have to do anything special** to use this, and you never have to worry about causing re-renders for updated data your component isn't actually interested in. | ||
- Not that it matters much with packages this tiny, but Statery is _even smaller_ than Zustand (at roughly 50% the size.) | ||
## SUMMARY | ||
```tsx | ||
import { value makeStore, value useStore } from "statery" | ||
import { makeStore, useStore } from "statery" | ||
@@ -74,2 +62,4 @@ const store = makeStore({ counter: 0 }) | ||
For a more fully-featured example, please check out the [demo]. | ||
## BASIC USAGE | ||
@@ -104,5 +94,11 @@ | ||
If you're using TypeScript, the type of the store state will be inferred from the initial state; but you may also pass a type argument to `makeStore` to explicitly set the type of the store: | ||
```ts | ||
const store = makeStore<{ count: number }>({ count: 0 }) | ||
``` | ||
### Updating the Store | ||
Update the store contents using its `set` function: | ||
The store object's `set` function will update the store's state and notify any listeners who have subscribed to changes: | ||
@@ -147,3 +143,3 @@ ```tsx | ||
When any of the data your components access changes in the store, they will automatically re-render. | ||
When any of the store's properties that your component accesses are updated, they will automatically re-render, automatically receiving the new state. | ||
@@ -194,10 +190,10 @@ ### Reading from a Store (without React) | ||
...as well as React components, which will automatically be rerendered if any of the underlying data changes: | ||
...as well as React components, which will automatically be re-rendered if any of the underlying data changes: | ||
```tsx | ||
const BuyHouseButton = () => { | ||
const proxy = useStore(store) | ||
const store = useStore(store) | ||
return ( | ||
<button onClick={buyHouse} disabled={!canBuyHouse(proxy)}> | ||
<button onClick={buyHouse} disabled={!canBuyHouse(store)}> | ||
Buy House (5g, 5w) | ||
@@ -248,25 +244,2 @@ </button> | ||
### Async updates | ||
Your Statery store doesn't know or care about asynchronicity -- simply call `set` whenever your data is ready: | ||
```ts | ||
const fetchPosts = async () => { | ||
const posts = await loadPosts() | ||
store.set({ posts }) | ||
} | ||
``` | ||
### TypeScript support | ||
Statery is written in TypeScript, and its stores are fully typed. `useStore` knows about the structure of your store, and if you're about to update a store with a property that it doesn't know about, TypeScript will warn you. | ||
If the state structure `makeStore` infers from its initial state argument is not what you want, you can explicitly pass a store type to `makeStore`: | ||
```tsx | ||
const store = makeStore<{ name?: string }>({}) | ||
store.set({ name: "Statery" }) | ||
store.set({ foo: 123 }) | ||
``` | ||
## NOTES | ||
@@ -282,3 +255,3 @@ | ||
Statery is written and maintained by Hendrik Mans. [Get in touch on Twitter](https://twitter.com/hmans)! | ||
Statery is written and maintained by [Hendrik Mans](https://www.hmans.dev/). | ||
@@ -285,0 +258,0 @@ [demo]: https://codesandbox.io/s/statery-clicker-game-hjxk3?file=/src/App.tsx |
18
10
34034
700
253