Skip to content

Instantly share code, notes, and snippets.

@jmfrancois
Last active March 24, 2020 08:56
Show Gist options
  • Save jmfrancois/a728521a1cee561e5fb8370b2882a863 to your computer and use it in GitHub Desktop.
Save jmfrancois/a728521a1cee561e5fb8370b2882a863 to your computer and use it in GitHub Desktop.
This gist let you use React into shadowDOM
/**
* IMPORTANT NOTE.
* Because react use synthetic event we have to patch the shadowDOM
* The following patch has to be applied just after the attachShadow
* and you must ban the use document.createElement. Use shadowRoot.createElement instead.
* Source: https://github.com/facebook/react/issues/9242
*/
function changeOwnerDocumentToShadowRoot(element, shadowRoot) {
Object.defineProperty(element, 'ownerDocument', {value: shadowRoot});
}
function augmentAppendChildWithOwnerDocument(elem, shadowRoot) {
const origAppChild = elem.appendChild;
const propDesc = Object.getOwnPropertyDescriptor(elem, 'appendChild');
if (!propDesc || propDesc.writable) {
Object.defineProperty(elem, 'appendChild', {
value: function (child) {
changeOwnerDocumentToShadowRoot(child, shadowRoot);
origAppChild?.call(elem, child);
}
});
}
}
function augmentCreateElementWithOwnerDocument(shadowRoot, createFnName) {
const originalCreateFn = document[createFnName];
shadowRoot[createFnName] = (...args) => {
const element = originalCreateFn.call(document, ...args);
changeOwnerDocumentToShadowRoot(element, shadowRoot);
augmentAppendChildWithOwnerDocument(element, shadowRoot);
return element;
};
}
export function patchShadowForReact(shadowRoot) {
augmentCreateElementWithOwnerDocument(shadowRoot, 'createElement');
augmentCreateElementWithOwnerDocument(shadowRoot, 'createElementNS');
augmentCreateElementWithOwnerDocument(shadowRoot, 'createTextNode');
}
import React from 'react';
import ReactDOM from 'react-dom';
import patchShadowForReact from './patchShadowForReact';
function MyButton(props) {
return <button type={prop.type || 'button'} onClick={e => props.onClick(e)}>{ props.label }</button>
}
class ShadowButton extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
if (!this.shadowRoot) {
this.attachShadow({ mode: "open" });
patchShadowForReact(this.shadowRoot);
this.root = this.shadowRoot.createElement('div');
this.shadowRoot.appendChild(this.root)
this.render();
}
}
onClick(e) {
e.preventDefault();
e.stopPropagation();
console.log('click');
}
render() {
function Demo() {
return (
<React.Fragment>
<h1>Hello button</h1>
<MyButton label="Hello world" onClick={(e) => this.onClick(e)} />
</React.Fragment>
);
}
ReactDOM.render(<Demo />, this.root);
}
}
customElements.define('my-button', ShadowButton);
const btn = document.createElement('my-button');
document.body.appendChild(btn);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment