UNPKG

react-singleton-hook

Version:
233 lines (214 loc) 8.37 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('react-dom')) : typeof define === 'function' && define.amd ? define(['exports', 'react', 'react-dom'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ReactSingletonHook = {}, global.React, global.ReactDOM)); })(this, (function (exports, React, require$$0) { 'use strict'; function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var React__default = /*#__PURE__*/_interopDefaultLegacy(React); var require$$0__default = /*#__PURE__*/_interopDefaultLegacy(require$$0); 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); } var SingleItemContainer = function SingleItemContainer(_ref) { var initValue = _ref.initValue, useHookBody = _ref.useHookBody, applyStateChange = _ref.applyStateChange; var lastState = React.useRef(initValue); if (typeof useHookBody !== 'function') { throw new Error("function expected as hook body parameter. got " + typeof useHookBody); } var val = useHookBody(); //useLayoutEffect is safe from SSR perspective because SingleItemContainer should never be rendered on server React.useLayoutEffect(function () { if (lastState.current !== val) { lastState.current = val; applyStateChange(val); } }, [applyStateChange, val]); return null; }; var createRoot; var m = require$$0__default["default"]; { var i = m.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; createRoot = function(c, o) { i.usingClientEntryPoint = true; try { return m.createRoot(c, o); } finally { i.usingClientEntryPoint = false; } }; } var warning = function warning(message) { if (console && console.warn) { console.warn(message); } }; // from https://github.com/purposeindustries/window-or-global/blob/master/lib/index.js // avoid direct usage of 'window' because `window is not defined` error might happen in babel-node var globalObject = typeof self === 'object' && self.self === self && self || typeof global === 'object' && global.global === global && global || this; var batch = function batch(cb) { return require$$0.unstable_batchedUpdates(cb); }; var mount = function mount(C) { if (globalObject.document && globalObject.document.createElement) { var container = globalObject.document.createElement('div'); var root = createRoot(container); root.render( /*#__PURE__*/React__default["default"].createElement(C, { automaticContainerInternalUseOnly: true })); } else { warning('Can not mount SingletonHooksContainer on server side. ' + 'Did you manage to run useEffect on server? ' + 'Please mount SingletonHooksContainer into your components tree manually.'); } }; var nextKey = 1; var automaticRender = false; var manualRender = false; var workingSet = []; var renderedContainers = []; var notifyContainersAsync = function notifyContainersAsync() { renderedContainers.forEach(function (updateRenderedHooks) { return updateRenderedHooks(); }); }; var SingletonHooksContainer = function SingletonHooksContainer(_ref) { var automaticContainerInternalUseOnly = _ref.automaticContainerInternalUseOnly; var _useState = React.useState([]), hooks = _useState[0], setHooks = _useState[1]; var currentHooksRef = React.useRef(); currentHooksRef.current = hooks; // if there was no automaticRender, and this one is not automatic as well if (!automaticContainerInternalUseOnly && automaticRender === false) { manualRender = true; } React.useEffect(function () { var mounted = true; function updateRenderedHooks() { if (!mounted) return; if (renderedContainers[0] !== updateRenderedHooks) { if (!automaticContainerInternalUseOnly && automaticRender === true) { warning('SingletonHooksContainer is mounted after some singleton hook has been used.' + 'Your SingletonHooksContainer will not be used in favor of internal one.'); } setHooks(function (_) { return []; }); return; } setHooks([].concat(workingSet)); } renderedContainers.push(updateRenderedHooks); notifyContainersAsync(); return function () { mounted = false; if (currentHooksRef.current.length > 0) { warning('SingletonHooksContainer is unmounted, but it has active singleton hooks. ' + 'They will be reevaluated once SingletonHooksContainer is mounted again'); } renderedContainers.splice(renderedContainers.indexOf(updateRenderedHooks), 1); notifyContainersAsync(); }; }, [automaticContainerInternalUseOnly]); return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, hooks.map(function (_ref2) { var hook = _ref2.hook, key = _ref2.key; return /*#__PURE__*/React__default["default"].createElement(SingleItemContainer, _extends({}, hook, { key: key })); })); }; var addHook = function addHook(hook) { var key = nextKey++; workingSet.push({ hook: hook, key: key }); // no container and no previous manually rendered containers if (renderedContainers.length === 0 && manualRender === false) { automaticRender = true; mount(SingletonHooksContainer); } notifyContainersAsync(); return function () { workingSet.splice(workingSet.findIndex(function (h) { return h.key === key; }), 1); notifyContainersAsync(); }; }; var singletonHook = function singletonHook(initValue, useHookBody, options) { if (options === void 0) { options = {}; } var mounted = false; var removeHook = undefined; var initStateCalculated = false; var lastKnownState = undefined; var consumers = []; var _options = options, _options$unmountIfNoC = _options.unmountIfNoConsumers, unmountIfNoConsumers = _options$unmountIfNoC === void 0 ? false : _options$unmountIfNoC; var applyStateChange = function applyStateChange(newState) { lastKnownState = newState; batch(function () { return consumers.forEach(function (c) { return c(newState); }); }); }; var stateInitializer = function stateInitializer() { if (!initStateCalculated) { lastKnownState = typeof initValue === 'function' ? initValue() : initValue; initStateCalculated = true; } return lastKnownState; }; return function () { var _useState = React.useState(stateInitializer), state = _useState[0], setState = _useState[1]; React.useEffect(function () { if (!mounted) { mounted = true; removeHook = addHook({ initValue: initValue, useHookBody: useHookBody, applyStateChange: applyStateChange }); } consumers.push(setState); if (lastKnownState !== state) { setState(lastKnownState); } return function () { consumers.splice(consumers.indexOf(setState), 1); if (consumers.length === 0 && unmountIfNoConsumers) { removeHook(); mounted = false; } }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return state; }; }; var ReactSingletonHook = { singletonHook: singletonHook, SingletonHooksContainer: SingletonHooksContainer }; exports.SingletonHooksContainer = SingletonHooksContainer; exports["default"] = ReactSingletonHook; exports.singletonHook = singletonHook; Object.defineProperty(exports, '__esModule', { value: true }); }));