Created
September 20, 2018 11:56
-
-
Save slorber/c422d6e180125a1baaebed678543793a to your computer and use it in GitHub Desktop.
ElementContainer.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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