Skip to content

Instantly share code, notes, and snippets.

@mitchellwarr
Last active May 9, 2023 22:38
Show Gist options
  • Save mitchellwarr/b86c760125e1ea110493aaa0a0c3ca02 to your computer and use it in GitHub Desktop.
Save mitchellwarr/b86c760125e1ea110493aaa0a0c3ca02 to your computer and use it in GitHub Desktop.
import ReactDOM from 'react-dom';
import { useEffect, useState, useMemo } from 'react';
import { makeRandomID } from 'lib/Utils';
const useDidUpdateEffect = (fn, state) => {
const isMounted = useMountingRef();
useEffect(
() => {
if (isMounted.current == 2) return fn();
},
state
);
};
const CHILDREN = {
};
const mountSelector = ({ selector, parentSelector, id }) => {
let container = document.createElement('div');
let parent = document.querySelector(selector);
if (!parent) {
parent = document.createElement('div');
parent.setAttribute('id', id);
document.querySelector(parentSelector).appendChild(parent);
}
parent.appendChild(container);
return {
container,
children: {}
};
};
const addChild = ({ selector, childID, renderer, parentSelector, id }) => {
if (!CHILDREN[selector] || !CHILDREN[selector].container) {
CHILDREN[selector] = mountSelector({ selector, parentSelector, id });
}
CHILDREN[selector].children[childID] = renderer;
ReactDOM.render(
<App Renderers={Object.entries(CHILDREN[selector].children)} />,
CHILDREN[selector].container
);
};
const updateChild = ({ selector, childID, renderer }) => {
CHILDREN[selector].children[childID] = renderer;
ReactDOM.render(
<App Renderers={Object.entries(CHILDREN[selector].children)} />,
CHILDREN[selector].container
);
};
const removeChild = ({ selector, childID }) => {
delete CHILDREN[selector].children[childID];
ReactDOM.render(
<App Renderers={Object.entries(CHILDREN[selector].children)} />,
CHILDREN[selector].container
);
if (Object.entries(CHILDREN[selector].children).length == 0) {
const { container } = CHILDREN[selector];
ReactDOM.unmountComponentAtNode(container);
if (container) container.parentNode.removeChild(container);
delete CHILDREN[selector];
}
};
const App = ({ Renderers }) => (
<>
{Renderers.map(
([key, Renderer]) => <Renderer key={key} />
)}
</>
);
export const usePortalRenderer = (renderer, { parentSelector = 'html > body', id } = {}) => {
const [childID] = useState(() => makeRandomID());
const selector = useMemo(() => parentSelector + (id? ` #${id}`: ''), [parentSelector, id]);
useEffect(
() => {
let oldSelector = selector;
addChild({
selector,
parentSelector,
id,
childID,
renderer
});
return () => {
removeChild({
selector: oldSelector,
childID
});
};
},
[selector, parentSelector, id]
);
useDidUpdateEffect(
() => {
updateChild({
selector,
childID,
renderer
});
},
[renderer]
);
};
export default usePortalRenderer;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment