Skip to content

Instantly share code, notes, and snippets.

@slorber
Created September 20, 2018 11:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save slorber/c422d6e180125a1baaebed678543793a to your computer and use it in GitHub Desktop.
Save slorber/c422d6e180125a1baaebed678543793a to your computer and use it in GitHub Desktop.
ElementContainer.js
import React from 'react';
const generateHolderKey = (() => {
let counter = 0;
return () => {
counter += 1;
return `holder_${counter}`;
};
})();
// See https://github.com/renatorib/react-powerplug/issues/171
export default class ElementContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
reactElementHolders: [],
};
this.api = {
add: this.add,
};
}
componentWillUnmount() {
this.setState = () => {};
}
addHolder = holder => {
this.setState({
reactElementHolders: this.state.reactElementHolders.concat(holder),
});
};
removeHolder = holder => {
this.setState({
reactElementHolders: this.state.reactElementHolders.filter(
h => h !== holder,
),
});
};
add = elementOrFunction => {
// We use a "holder" because we must inject the "remove" hook into the function,
// while we don't know yet which element should be removed at that time!
// holder pattern solves this circular dependency issue
const holder = {};
const addElement = element => {
if (!React.isValidElement(element)) {
throw new Error(`invalid react element => ${element}`);
}
holder.key = generateHolderKey();
holder.element = element;
this.addHolder(holder);
return element;
};
// Allow to pass a function provided with a remove hook
if (elementOrFunction instanceof Function) {
elementOrFunction = elementOrFunction(() => this.removeHolder(holder));
}
// allow element promises
const result =
elementOrFunction instanceof Promise
? elementOrFunction.then(addElement)
: addElement(elementOrFunction);
// result element. If it's a promise return promised element.
return result;
};
remove = element => {
const holder = this.state.reactElementHolders.find(
holder => holder.element === element,
);
if (!holder) {
throw new Error(`Can't remove element, not found. element=${element}`);
}
this.removeHolder(holder);
};
render() {
return (
<React.Fragment>
{this.state.reactElementHolders.map(holder => (
<React.Fragment key={holder.key}>{holder.element}</React.Fragment>
))}
{React.Children.only(this.props.children)(this.api)}
</React.Fragment>
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment