use-store
Version:
Shared, persistable React.js useState() hook effect, no context required
198 lines (158 loc) • 5.65 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.useStore = useStore;
exports["default"] = exports.globalStore = exports.GlobalStore = exports.Store = void 0;
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _react = require("react");
var GLOBALSTORAGE_PREFIX = '!ush::';
var debounce = function debounce(func) {
var delay = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 100;
var timer;
return function () {
var context = this;
var args = arguments;
clearTimeout(timer);
timer = setTimeout(function () {
return func.apply(context, args);
}, delay);
};
}; // https://stackoverflow.com/a/2117523/11599918
var uuid = function uuid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0,
v = c == 'x' ? r : r & 0x3 | 0x8;
return v.toString(16);
});
}; // individual Store implementation for tracking values/setters
var Store = function Store(_ref) {
var _this = this;
var _value = _ref.value,
namespace = _ref.namespace,
_options = _ref.options;
(0, _classCallCheck2["default"])(this, Store);
(0, _defineProperty2["default"])(this, "handleMessage", debounce(function (e) {
if (!e.data || e.data.id === _this.id) {
return;
}
_this.setState(e.data.message, {
broadcast: false
});
}, 300));
(0, _defineProperty2["default"])(this, "setState", function (value) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
broadcast: true
};
_this.state = value;
if (_this.options.persist) {
try {
localStorage.setItem(GLOBALSTORAGE_PREFIX + _this.namespace, JSON.stringify(value));
} catch (err) {
console.warn("[use-store-hook]: failed to persist", {
value: value,
err: err
});
}
}
_this.setters.forEach(function (setter) {
return setter(_this.state);
});
if (options.broadcast && _this.options.broadcast) {
_this.channel.postMessage({
id: _this.id,
message: value
});
}
});
this.state = _value;
this.id = uuid();
if (_options.persist) {
try {
var stored = localStorage.getItem(GLOBALSTORAGE_PREFIX + namespace);
if (stored !== null) {
this.state = JSON.parse(stored);
}
} catch (err) {}
}
if (_options.broadcast && window.BroadcastChannel) {
this.channel = new BroadcastChannel(GLOBALSTORAGE_PREFIX + namespace);
this.channel.addEventListener('message', this.handleMessage);
}
this.options = _options;
this.namespace = namespace;
this.setters = [];
}; // namespaced index of requested Stores
exports.Store = Store;
var GlobalStore = function GlobalStore() {
var _this2 = this;
(0, _classCallCheck2["default"])(this, GlobalStore);
(0, _defineProperty2["default"])(this, "set", function (namespace, value) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
if (_this2.hasOwnProperty(namespace)) {
_this2[namespace].setState(value);
} else {
_this2[namespace] = new Store({
value: value,
options: options,
namespace: namespace
});
}
});
(0, _defineProperty2["default"])(this, "clear", function (namespace) {
localStorage.removeItem(GLOBALSTORAGE_PREFIX + namespace);
});
(0, _defineProperty2["default"])(this, "persist", function () {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return _this2.set.apply(_this2, args.concat([{
persist: true
}]));
});
}; // shared instantiation of GlobalStore
exports.GlobalStore = GlobalStore;
var globalStore = new GlobalStore(); // the actual hook
exports.globalStore = globalStore;
function useStore(namespace, value) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var whichStore = undefined;
if (!namespace) {
throw new Error('no namespace provided to useStore... try using useState() instead?');
}
if (globalStore.hasOwnProperty(namespace)) {
whichStore = globalStore[namespace];
} else {
whichStore = globalStore[namespace] = new Store({
value: value,
options: options,
namespace: namespace
});
}
var _useState = (0, _react.useState)(whichStore.state),
_useState2 = (0, _slicedToArray2["default"])(_useState, 2),
state = _useState2[0],
set = _useState2[1];
if (whichStore.setters.indexOf(set) === -1) {
whichStore.setters.push(set);
}
(0, _react.useEffect)(function () {
return function () {
whichStore.setters = whichStore.setters.filter(function (setter) {
return setter !== set;
});
};
}, []);
var magicSetter = function magicSetter(setter) {
return function (e) {
(0, _typeof2["default"])(e) === 'object' && (e.nativeEvent || e.constructor.name === 'SyntheticEvent') && e.target ? setter(e.target.value) : setter(e);
};
};
return [state, magicSetter(whichStore.setState)];
}
var _default = useStore;
exports["default"] = _default;