rportal
Version:
is a simple React package used to render components outside default React top-down rendering hierarchy. With this package you can model any logical component hierarchy you want. You simply put 2 (or more) portals somewhere in your application and children
83 lines (72 loc) • 2.63 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = Portal;
exports.globalStore = void 0;
var _react = _interopRequireDefault(require("react"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const globalStore = () => {
let keys = {};
let watchers = {};
const set = (key, value)=>{
const oldValue = keys[key];
keys[key] = value;
(watchers[key]||[]).forEach(fn=>fn(value, oldValue));
return oldValue;
};
return {
get: key => keys[key],
set,
unset: key => {
set(key, undefined);
delete keys[key];
},
keys: () => Object.keys(keys),
watch: (key, watcher) => {
if (!watchers[key]) watchers[key] = [];
watchers[key] = [...watchers[key], watcher];
return watcher;
},
unwatch: (key, watcher) => {
watchers[key] = [...watchers[key]].filter(_w => _w !== watcher);
}
};
};
exports.globalStore = globalStore;
const globalState = globalStore();
globalState.set('_id', 1);
function Portal(props) {
const [id, _] = _react.default.useState(globalState.set('_id', globalState.get('_id') + 1));
const [counter, setCounter] = _react.default.useState(0);
_react.default.useEffect(() => {
if (props.type === 'container') {
const fn = () => setCounter(counter + 1);
globalState.watch(props.id, fn);
return () => globalState.unwatch(props.id, fn);
}
}, [props.type, counter]);
_react.default.useEffect(() => {
const curChildren = globalState.get(props.id) || [];
const curIndex = curChildren.findIndex(row => row.id === id);
const row = {
id,
children: props.children
};
globalState.set(props.id, curIndex === -1 ? [...curChildren, row] : [...curChildren.slice(0, curIndex), row, ...curChildren.slice(curIndex + 1)]);
return () => {
globalState.set(props.id, (globalState.get(props.id) || []).filter(row => row.id !== id));
};
}, [props.children]);
if (props.type === 'item') {
return null;
} else if (props.type === 'container') {
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, (globalState.get(props.id) || []).map(row => /*#__PURE__*/_react.default.createElement(_react.default.Fragment, {
key: row.id
}, _react.default.Children.map(row.children, child => /*#__PURE__*/_react.default.isValidElement(child) ? /*#__PURE__*/_react.default.cloneElement(child, { ...props,
...child.props
}) : child))));
} else {
throw new Error('Portal must be either a container or item');
}
}