Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shadow DOM and ReactJS

I am using web components in my application. And in a web component, I need to insert a react component. Web Component has Shadow DOM. When I try to render the react component using following I get the error.

comp = React.createElement(ReactElem, {
    config: config,
    onRender: handleRender
});

ReactDOM.render(comp , self.shadowRoot.querySelector('#app1'));

Error

target container is not a dom element

I tried to use content of Web component API but then it gets rendered on top rather inside component. Any leads how can make React component to get rendered inside shadow DOM?

like image 307
thecodejack Avatar asked Feb 16 '17 13:02

thecodejack


People also ask

Does React work with shadow DOM?

ReactJS does not use the shadow DOM for making it faster, it completely relies on the virtual one. But anyone can use this feature of HTML5 intentionally if required.

What is shadow DOM in ReactJS?

The Shadow DOM is a browser technology designed primarily for scoping variables and CSS in web components. The virtual DOM is a concept implemented by libraries in JavaScript on top of browser APIs.

Why does React use a shadow DOM?

In short, the shadow DOM is a browser technology whose main objective is to provide encapsulation when creating elements. On the other hand, the virtual DOM is managed by JavaScript libraries—e.g., React—and it's mostly a strategy to optimize performance.

Does ReactJS use virtual DOM?

Virtual DOM: React uses Virtual DOM exists which is like a lightweight copy of the actual DOM(a virtual representation of the DOM). So for every object that exists in the original DOM, there is an object for that in React Virtual DOM.


2 Answers

If you want to insert it inside the shadow DOM of a web component, first select the component (e.g. with querySelector) and then its containing shadow (shadowRoot). Simplified example:

// Create React Element.
class Example extends React.Component {
  onClick() {
    console.log('Shadow!')
  }
  render() {
    return(
      <div onClick={this.onClick}>Hello World!</div>
    );
  }
}

// Create web component with target div inside it.
const container = document.createElement('app');
document.body.appendChild(container);

// Add shadow root to component.
const shadow = document.querySelector('app').attachShadow({ mode: 'open' });

// Select the web component, then the shadowRoot.
const target = document.querySelector('app').shadowRoot;

ReactDOM.render(<Example />, target);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
like image 59
Fabian Schultz Avatar answered Oct 20 '22 05:10

Fabian Schultz


Hey my friend, I took the time and crafted this for us:

// ShadowRoot.js

import React from "react";

export class ShadowRoot extends React.Component {

    attachShadow(host) {
        if (host == null) {
            return;
        }
        host.attachShadow({mode: "open"});
        host.shadowRoot.innerHTML = host.innerHTML;
        host.innerHTML = "";
    }

    render() {
        return (
            <span ref={this.attachShadow}>
                {this.props.children}
            </span>
        );
    }

}

Use it like this:

<ShadowRoot>
    // put stuff here you want inside shadow root
</ShadowRoot>

2 things to consider:

  • the class React.Component is working better than the hook equivalent
  • the innerHTML thing is kinda hacky, and state updates are not working with this component
like image 32
Hannes Schneidermayer Avatar answered Oct 20 '22 05:10

Hannes Schneidermayer