Skip to content

Instantly share code, notes, and snippets.

@vincentriemer
Created October 29, 2018 17:44
Show Gist options
  • Save vincentriemer/a7a1261d657da33c983956f5f7b5a0ca to your computer and use it in GitHub Desktop.
Save vincentriemer/a7a1261d657da33c983956f5f7b5a0ca to your computer and use it in GitHub Desktop.
Hooks implementation of `react-gateway`
import React, { useContext, useLayoutEffect, useEffect, useRef } from "react";
import GatewayRegistry from "./GatewayRegistry";
import { GatewayContext } from "./GatewayProvider";
const Gateway = props => {
const gatewayRegistry = useContext(GatewayContext);
// keeping track of previous `into` prop
const prevIntoRef = useRef();
useLayoutEffect(() => {
prevIntoRef.current = props.into;
});
const prevInto = prevIntoRef.current;
// componentDidMount/componentWillUnmount
const idRef = useRef();
useLayoutEffect(() => {
const id = gatewayRegistry.register(props.into, props.children);
idRef.current = id;
return () => {
gatewayRegistry.unregister(props.into, id);
};
}, []);
// componentDidUpdate
useLayoutEffect(() => {
prevInto != null && gatewayRegistry.clearChild(prevInto, idRef.current);
gatewayRegistry.addChild(props.into, idRef.current, props.children);
});
return null;
};
export default Gateway;
import React, { useContext, useLayoutEffect, useState } from "react";
import GatewayRegistry from "./GatewayRegistry";
import { GatewayContext } from "./GatewayProvider";
const GatewayDest = props => {
const gatewayRegistry = useContext(GatewayContext);
const [children, setChildren] = useState(null);
useLayoutEffect(() => {
gatewayRegistry.addContainer(props.name, setChildren);
return () => {
gatewayRegistry.removeContainer(props.name);
};
}, []);
const { component, tagName, ...attrs } = props;
delete attrs.name;
return React.createElement(component || tagName || "div", attrs, children);
};
export default GatewayDest;
import React, { useState } from "react";
import GatewayRegistry from "./GatewayRegistry";
export const GatewayContext = React.createContext(new GatewayRegistry());
const GatewayProvider = props => {
const [gatewayRegistry] = useState(new GatewayRegistry());
return (
<GatewayContext.Provider value={gatewayRegistry}>
{props.children}
</GatewayContext.Provider>
);
};
export default GatewayProvider;
export default class GatewayRegistry {
constructor() {
this._containers = {};
this._children = {};
// Unique key for children of a gateway
this._currentId = 0;
}
_renderContainer(name) {
if (!this._containers[name] || !this._children[name]) {
return;
}
this._containers[name](
Object.keys(this._children[name])
.sort()
.map(id => this._children[name][id])
);
}
addContainer(name, setChildren) {
this._containers[name] = setChildren;
this._renderContainer(name);
}
removeContainer(name) {
this._containers[name] = null;
}
addChild(name, gatewayId, child) {
this._children[name][gatewayId] = child;
this._renderContainer(name);
}
clearChild(name, gatewayId) {
delete this._children[name][gatewayId];
}
register(name, child) {
this._children[name] = this._children[name] || {};
const gatewayId = `${name}_${this._currentId}`;
this._children[name][gatewayId] = child;
this._currentId += 1;
return gatewayId;
}
unregister(name, gatewayId) {
this.clearChild(name, gatewayId);
this._renderContainer(name);
}
}
export { default as Gateway } from "./Gateway";
export { default as GatewayDest } from "./GatewayDest";
export { default as GatewayProvider } from "./GatewayProvider";
export { default as GatewayRegistry } from "./GatewayRegistry";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment