impact-context
Version:
Reactive contexts for React
148 lines • 5.61 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.context = exports.componentConsumptionHooks = exports.cleanup = exports.ContextProvider = exports.getActiveContextContainer = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const currentContextContainer = [];
function getActiveContextContainer() {
return currentContextContainer[currentContextContainer.length - 1];
}
exports.getActiveContextContainer = getActiveContextContainer;
class ContextContainer {
get isDisposed() {
return this._isDisposed;
}
constructor(ref, constr, _parent) {
this._parent = _parent;
this._disposers = new Set();
this._isDisposed = false;
this._state = {
isResolved: false,
ref,
constr,
};
}
registerCleanup(cleaner) {
this._disposers.add(cleaner);
}
resolve(context) {
if (this._resolvementError) {
throw this._resolvementError;
}
if (this._state.isResolved && context === this._state.ref) {
return this._state.value;
}
if (!this._state.isResolved && this._state.ref === context) {
try {
currentContextContainer.push(this);
this._state = {
isResolved: true,
value: this._state.constr(),
ref: context,
};
currentContextContainer.pop();
return this._state.value;
}
catch (e) {
this._resolvementError =
new Error(`Could not initialize context "${context === null || context === void 0 ? void 0 : context.name}":
${String(e)}`);
throw this._resolvementError;
}
}
if (!this._parent) {
throw new Error(`The context "${context.name}" is not provided`);
}
return this._parent.resolve(context);
}
clear() {
this._disposers.forEach((cleaner) => {
cleaner();
});
}
dispose() {
this.clear();
this._isDisposed = true;
}
}
const reactContext = (0, react_1.createContext)(null);
class ContextProvider extends react_1.Component {
componentWillUnmount() {
this.container.dispose();
}
render() {
// React can keep the component reference and mount/unmount it multiple times. Because of that
// we need to ensure to always have a hooks container instantiated when rendering, as it could
// have been disposed due to an unmount
if (!this.container || this.container.isDisposed) {
this.container = new ContextContainer(this.props.context, () => this.props.context(this.props.props),
// eslint-disable-next-line
// @ts-ignore
this.context);
}
return ((0, jsx_runtime_1.jsx)(reactContext.Provider, { value: this.container, children: this.props.children }));
}
}
exports.ContextProvider = ContextProvider;
ContextProvider.contextType = reactContext;
function cleanup(cleaner) {
const activeContextContainer = getActiveContextContainer();
if (!activeContextContainer) {
throw new Error("You are cleaning up in an invalid context");
}
activeContextContainer.registerCleanup(cleaner);
}
exports.cleanup = cleanup;
exports.componentConsumptionHooks = {
isConsuming: false,
onConsume: () => { },
onConsumed: () => { },
};
function context(context) {
const useReactiveContext = () => {
const activeContextContainer = getActiveContextContainer();
if (!activeContextContainer) {
if (!exports.componentConsumptionHooks.isConsuming) {
exports.componentConsumptionHooks.isConsuming = true;
exports.componentConsumptionHooks.onConsume();
}
const contextContainer = (0, react_1.useContext)(reactContext);
if (!contextContainer) {
throw new Error("You are using a store outside its provider");
}
return contextContainer.resolve(context);
}
return activeContextContainer.resolve(context);
};
useReactiveContext.Provider = (props) => {
// To avoid TSLIB
const propsCopy = Object.assign({}, props);
const children = propsCopy.children;
delete propsCopy.children;
return ((0, jsx_runtime_1.jsx)(ContextProvider, { props: propsCopy, context: context, children: children }));
};
// @ts-ignore
useReactiveContext.Provider.displayName =
context.name || "ReactiveContextProvider";
return useReactiveContext;
}
exports.context = context;
if (typeof window !== "undefined") {
let currentDispatcher = null;
Object.defineProperty(react_1.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentDispatcher, "current", {
get() {
return currentDispatcher;
},
set(nextDispatcher) {
currentDispatcher = nextDispatcher;
if (exports.componentConsumptionHooks.isConsuming &&
// When the hooks has the same implementation, it is to throw an error, meaning
// we are done consuming a hooks context
currentDispatcher.useReducer === currentDispatcher.useEffect) {
exports.componentConsumptionHooks.isConsuming = false;
exports.componentConsumptionHooks.onConsumed();
}
},
});
}
//# sourceMappingURL=index.js.map