preact-context
Version:
React's new Context Api for preact
101 lines (100 loc) • 4.35 kB
JavaScript
import * as tslib_1 from "tslib";
import { h, Component } from "preact";
import { createEmitter, noopEmitter } from "./context-value-emitter";
import { getOnlyChildAndChildren } from "./utils";
function getRenderer(props) {
var child = getOnlyChildAndChildren(props).child;
// TODO: "render" in props check is only done to make TS happy
return child || ("render" in props && props.render);
}
var MAX_SIGNED_31_BIT_INT = 1073741823;
var defaultBitmaskFactory = function () { return MAX_SIGNED_31_BIT_INT; };
var ids = 0;
function _createContext(value, bitmaskFactory) {
var key = "_preactContextProvider-" + ids++;
var Provider = /** @class */ (function (_super) {
tslib_1.__extends(Provider, _super);
function Provider(props) {
var _this = _super.call(this, props) || this;
_this._emitter = createEmitter(props.value, bitmaskFactory || defaultBitmaskFactory);
return _this;
}
Provider.prototype.getChildContext = function () {
var _a;
return _a = {}, _a[key] = this._emitter, _a;
};
Provider.prototype.componentDidUpdate = function () {
this._emitter.val(this.props.value);
};
Provider.prototype.render = function () {
var _a = getOnlyChildAndChildren(this.props), child = _a.child, children = _a.children;
if (child) {
return child;
}
// preact does not support fragments,
// therefore we wrap the children in a span
return h("span", null, children);
};
return Provider;
}(Component));
var Consumer = /** @class */ (function (_super) {
tslib_1.__extends(Consumer, _super);
function Consumer(props, ctx) {
var _this = _super.call(this, props, ctx) || this;
_this._updateContext = function (value, bitmask) {
var unstable_observedBits = _this.props.unstable_observedBits;
var observed = unstable_observedBits === undefined || unstable_observedBits === null
? MAX_SIGNED_31_BIT_INT
: unstable_observedBits;
observed = observed | 0;
if ((observed & bitmask) === 0) {
return;
}
_this.setState({ value: value });
};
_this.state = { value: _this._getEmitter().val() || value };
return _this;
}
Consumer.prototype.componentDidMount = function () {
this._getEmitter().register(this._updateContext);
};
Consumer.prototype.shouldComponentUpdate = function (nextProps, nextState) {
return (this.state.value !== nextState.value ||
getRenderer(this.props) !== getRenderer(nextProps));
};
Consumer.prototype.componentWillUnmount = function () {
this._getEmitter().unregister(this._updateContext);
};
Consumer.prototype.componentDidUpdate = function (_, __, prevCtx) {
var previousProvider = prevCtx[key];
if (previousProvider === this.context[key]) {
return;
}
(previousProvider || noopEmitter).unregister(this._updateContext);
this.componentDidMount();
};
Consumer.prototype.render = function () {
// TODO: "render" in props check is only done to make TS happy
var render = "render" in this.props && this.props.render;
var r = getRenderer(this.props);
if (render && render !== r) {
console.warn("Both children and a render function are defined. Children will be used");
}
if (typeof r === "function") {
return r(this.state.value);
}
console.warn("Consumer is expecting a function as one and only child but didn't find any");
};
Consumer.prototype._getEmitter = function () {
return this.context[key] || noopEmitter;
};
return Consumer;
}(Component));
return {
Provider: Provider,
Consumer: Consumer
};
}
// named and default export in order to have less problems with bundlers
export default _createContext;
export var createContext = _createContext;