Giter VIP home page Giter VIP logo

react-web-component's Introduction

React Web Component Logo

React Web Component

Create Web Components with React

Installation

React Web Component is available as the react-web-component package on npm.

yarn add react-web-component

Basic Usage

To create a web component with react-web-component, simply pass a React component as the first parameter to ReactWebComponent.create and the name of the web component you would like to create as the second parameter.

import React from 'react';
import ReactWebComponent from 'react-web-component';

class App extends React.Component {
  render() {
    return <div>Hello World!</div>;
  }
}

ReactWebComponent.create(<App />, 'my-component');

Then in your HTML simply use your web component, in this case named my-component.

<my-component></my-component>

By default the shadowRoot is enabled. This allows for styles isolation and prevents component styles from bleeding out to other parts of the application. It also prevents outer styles from affecting the web component you are creating.

In case that you want your component to inherit styles from the parent you can opt-out of the shadowRoot. To do that you can pass an optional parameter to the create method:

ReactWebComponent.create(<App />, 'my-component', true);

It is also possible to create multiple web components in a single project and pass on props:

import React from 'react';
import ReactWebComponent from 'react-web-component';

class MyComponent extends React.Component {
  render() {
    return <div>Hello World!</div>;
  }
}

class AnotherComponent extends React.Component {
  render() {
    return <div>Hello {this.props.name}!</div>;
  }
}

ReactWebComponent.create(<MyComponent />, 'my-component');
ReactWebComponent.create(<AnotherComponent name="Mars" />, 'another-component');

Note that react-web-component does not limit you in the complexity of your React component. You can pass an entire single page application in your web component if you need to.

Lifecycle methods

Web Components have their own lifecycle methods so we proxy them to your React Component. The following table shows which Web Component lifecycle method will trigger which lifecycle method on your React Component:

Web Component           React Component
attachedCallback webComponentAttached
connectedCallback webComponentConnected
disconnectedCallback webComponentDisconnected
attributeChangedCallback webComponentAttributeChanged
adoptedCallback webComponentAdopted

Example:

import React from 'react';
import ReactWebComponent from 'react-web-component';

class App extends React.Component {

  componentDidMount() {
    // Regular React lifecycle method
  }

  webComponentAttached() {
    // will be called when the Web Component has been attached
  }

  render() {
    return <div>Hello World!</div>;
  }
}

ReactWebComponent.create(<App />, 'my-component');

Parameters passed to the web component methods will also be proxied to the react component.

Adding CSS to your web component using react-web-component-style-loader

The challenge of adding CSS to your web component is that (as compared to a regular React component) you cannot simply put your CSS anywhere in your site, you need to inject it into the shadow dom of your web component, while at the same time you'll still want to use state of the art tooling (i.e. webpack) and create component based (S)CSS files.

We've got you covered. You can use the react-web-component-style-loader webpack loader module that in combination with react-web-component will inject the css imported anywhere in your project into your web component. Here is a quick example:

App.js

import React from 'react';
import './app.css';

export default class App extends React.Component {
  render() {
    return <div>Hello World!</div>;
  }
}

index.js

import React from 'react';
import ReactWebComponent from 'react-web-component';
import App from './App';
import './index.css';

ReactWebComponent.create(<App />, 'my-component');

Using the react-web-component-style-loader both the CSS from app.css as well as index.css will end up in your web component. Read more about it in the react-web-component-style-loader repository.

Adding CSS to your web component imperatively

  • todo: allow ReactWebComponent.create(<App />, 'my-component', { style: '' });
  • todo: allow ReactWebComponent.create(<App />, 'my-component', { cssFile: '' });

Usage with react-router

react-web-component works with react-router with one restriction: Since web components live within a host website you should now modify the URL of the website when the web component does internal routing. Luckily react-router comes with an in memory router that does not alter the URL and keeps the state of the router internally.

import React from 'react';
import {
  MemoryRouter as Router,
  Route,
  Link,
  withRouter
} from 'react-router-dom';
import ReactWebComponent from 'react-web-component';
import { Home, About, Topics } from './components';

const App = withRouter(({ history}) => (
  <div>
    <ul>
      <li><Link to="/">Home</Link></li>
      <li><Link to="/about">About</Link></li>
      <li><Link to="/topics">Topics</Link></li>
    </ul>
    <Route exact path="/" component={Home}/>
    <Route path="/about" component={About}/>
    <Route path="/topics" component={Topics}/>
  </div>
))

const RoutedApp = () => (
  <Router initialEntries={[ '/' ]}>
    <App />
  </Router>
);

ReactWebComponent.create(<RoutedApp />, 'my-routed-component');

Issues

If you encounter that some events won't be triggered

If you encounter that an event that you rely on will not get triggered this is probably our fault (or Facebook's to be precise). React does not catch events inside shadow dom. A bug ticket has been filed for this at react#10422. For now, we have created a workaround for this in the shape of another npm package that this library depends on. In this library we listen for events on the DOM level and pass them on to React. If an event does not get triggered it probably means we have forgotten it in the list of events that we subscribe to. This is very inconvenient but not a problem. We can simply add it. Open an issue or a PR at WeltN24/react-shadow-dom-retarget-events and we will gladly add the event.

Maintainers


Lukas Bombach

react-web-component's People

Contributors

lukasbombach avatar mnemanja avatar newying61 avatar tdt17 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-web-component's Issues

Uncaught ReferenceError: regeneratorRuntime is not defined

It works when the component I create is a dumb component, but when I try to create a more complex react component I get "Uncaught ReferenceError: regeneratorRuntime is not defined"
I've been searching and it seems there's an issue with babel, but I still don't understand which.

Could lend a hand on this?

Material UI compatibility

Hello,

When using React Web Component I am unable to get Material UI to work with it.
No styles are recognized, and when I try using Dialog component, I get:
index.js:32 Uncaught TypeError: Cannot read property 'webComponentAttributeChanged' of undefined
at callLifeCycleHook (index.js:32)...

I guess it has something to do with Shadow DOM and CSS.
Is there any workaround for this?

React props are not available when arguments are set in html tag

Do you want to request a feature or report a bug?

bug

What is the current behavior?

The props passed to the dom element is not available in react via this.props.attribute

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem via https://jsfiddle.net or similar (template for React 16: https://jsfiddle.net/Luktwrdm/, template for React 15: https://jsfiddle.net/hmbg7e9w/).

The issue is visible here : https://jsfiddle.net/84v837e9/187

What is the expected behavior?

When an attribute is passed in an html tag, it should be passed to the react props:

    <App name="world"/>

Should returns the html tag containing "world" inside this.props.name

    <h1>Hello, world</h1>....

The props is empty inside the App react element

Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?

All browsers, tested with react 16

Injecting Third Party Component Styles into Shadow DOM

Currently my React component uses third party components (Material UI mostly) which have their own styling.

For example:

import React from 'react';
import PropTypes from 'prop-types';
import Button from '@material-ui/core/Button';
....

When it is wrapped as a Web Component, the styling is injected to the local DOM (multiple style tags) , rather than to the Shadow DOM of the new custom element. Any ideas on how to solve this?

Cannot read property 'webComponentConstructed' of null

While using the react-router I am getting this error.

'use strict';var _createClass=function(){function a(a,b){for(var c,d=0;d<b.length;d++)c=b[d],c.enumerable=c.enumerable||!1,c.configurable=!0,'value'in c&&(c.writable=!0),Object.defineProperty(a,c.key,c)}return function(b,c,d){return c&&a(b.prototype,c),d&&a(b,d),b}}();function _classCallCheck(a,b){if(!(a instanceof b))throw new TypeError('Cannot call a class as a function')}function _possibleConstructorReturn(a,b){if(!a)throw new ReferenceError('this hasn't been initialised - super() hasn't been called');return b&&('object'==typeof b||'function'==typeof b)?b:a}function _inherits(a,b){if('function'!=typeof b&&null!==b)throw new TypeError('Super expression must either be null or a function, not '+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.proto=b)}function _CustomElement(){return Reflect.construct(HTMLElement,[],this.proto.constructor)}Object.setPrototypeOf(_CustomElement.prototype,HTMLElement.prototype),Object.setPrototypeOf(_CustomElement,HTMLElement);var React=require('react'),ReactDOM=require('react-dom'),retargetEvents=require('react-shadow-dom-retarget-events'),getStyleElementsFromReactWebComponentStyleLoader=require('./getStyleElementsFromReactWebComponentStyleLoader'),extractAttributes=require('./extractAttributes');require('@webcomponents/shadydom'),require('@webcomponents/custom-elements'),module.exports={create:function create(a,b){function c(a){f.webComponentConstructed&&f.webComponentConstructed.apply(f,[a])}function d(a,b){var c=g[a];c&&f&&f[c]&&f[c].apply(f,b||[])}var e=!(2<arguments.length&&arguments[2]!==void 0)||arguments[2],f=void 0,g={attachedCallback:'webComponentAttached',connectedCallback:'webComponentConnected',disconnectedCallback:'webComponentDisconnected',attributeChangedCallback:'webComponentAttributeChanged',adoptedCallback:'webComponentAdopted'},h=function(b){function g(){return _classCallCheck(this,g),_possibleConstructorReturn(this,(g.proto||Object.getPrototypeOf(g)).apply(this,arguments))}return _inherits(g,b),_createClass(g,[{key:'connectedCallback',value:function connectedCallback(){var b=this,g=b;if(e){var h=b.attachShadow({mode:'open'});g=document.createElement('div');var i=getStyleElementsFromReactWebComponentStyleLoader();i.forEach(function(a){h.appendChild(a.cloneNode(h))}),h.appendChild(g),retargetEvents(h)}ReactDOM.render(React.cloneElement(a,extractAttributes(b)),g,function(){f=this,c(b),d('connectedCallback')})}},{key:'disconnectedCallback',value:function disconnectedCallback(){d('disconnectedCallback')}},{key:'attributeChangedCallback',value:function attributeChangedCallback(a,b,c,e){d('attributeChangedCallback',[a,b,c,e])}},{key:'adoptedCallback',value:function adoptedCallback(a,b){d('adoptedCallback',[a,b])}}]),g}(_CustomElement);customElements.define(b,h)}};

Please let me know what I am doing wrong.
For the other case where router is not used it is working perfectly fine.

Export Web Component

Will this library export the web component so the component can be used in another React app? If so, where will the component be written?

Have to ask because I didn't see this in the documentation.

TypeError: f is null - When trying to use react-web-component with Create React App base application

Hi,

I'm trying to use react-web-component with Create React App to test some stuff, but I'm getting stuck right in the beggining. Am I doing something wrong?

// general
import App from './App';
import ReactWebComponent from 'react-web-component';
import React from 'react';
// styles
import './index.css';

ReactWebComponent.create(<App />, 'my-component');

Here is the generated HTML

<html lang="en"><head>
    <meta charset="utf-8">
    <link rel="icon" href="/favicon.ico">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="theme-color" content="#000000">
    <meta name="description" content="Web site created using create-react-app">
    <link rel="apple-touch-icon" href="/logo192.png">
    <!--
      manifest.json provides metadata used when your web app is installed on a
      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
    -->
    <link rel="manifest" href="/manifest.json">
    <!--
      Notice the use of  in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
    <title>React App</title>
</head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <my-component></my-component> <!-- Added by me afterwards directly on the browser -->
  <script src="/static/js/bundle.js"></script><script src="/static/js/0.chunk.js"></script><script src="/static/js/main.chunk.js"></script>

</body></html>

Then I get the following error on the browser console

image

Thanks for your help

How to support attributes with array ?

We have swiper component like

<Swiper items={[
   {
      src: 'img url'
   },
  {
      src: 'img url2'
   }
]} />

Now we change it to web-components

import { YYSwiper } from './Swipper';
import ReactWebComponent from 'react-web-component';
ReactWebComponent.create(<YYSwiper />, 'web-swiper');

And use like

<web-swiper
  vertical="false"
  items="[{\"src\"=\"\"}, {\"src\"=\"\"}]"
></web-swiper>

I need to JSON.parse(items) in ./Swipper'

Console warning about window.customElements.define

There is a warning about deprecation of registerElement method.

 [Deprecation] document.registerElement is deprecated and will be removed in M73, around March 2019. Please use window.customElements.define instead. See https://www.chromestatus.com/features/4642138092470272 for more details.

TypeError: Cannot read property 'create' of undefined

Error in Chrome trying to create a web-component with react and typescript

index.tsx:5 Uncaught TypeError: Cannot read property 'create' of undefined
at Object. (index.tsx:5)
at n (bootstrap:19)
at bootstrap:83
at header-component.min.js:1

i built a simple component:

import * as React from 'react';

export const HeaderComponent = () => {
  return <div>Header Component</div>;
};

and a index.tsx

import * as React from 'react';
import ReactWebComponent from 'react-web-component';
import { HeaderComponent } from './headercomponent';

ReactWebComponent.create(<HeaderComponent />, 'header-component');

i built it with webpack and the awesome-typescript-loader

this is my html file

<!DOCTYPE html>
<html lang="en">
  <head>
    <title></title>
    <script src="header-component.min.js"></script>
  </head>
  <body>
    <header-component></header-component>
  </body>
</html>

Is there Support for Typescript available?

how to import the ReactWebComponent?

In the basic usage example you define a file:

import React from 'react';
import ReactWebComponent from 'react-web-component';
 
class App extends React.Component {
  render() {
    return <div>Hello World!</div>;
  }
}
 
ReactWebComponent.create(<App />, 'my-component');

But how do I import it into my html file? I tried <link rel="import" href="../path/to/file.js">, and also adding export default ReactWebComponent.create(<App />, 'my-component'); to the js file. But nothing is working. Could you explain this in the docs for people new to Web Components?

support functional components

I had to wrap my functional react component in a class react component in order to get this neat tools to work, it would be nice if this weren't necessary so that I could use functional components directly, thank you

useShadowDom documentation updates

  1. the d.ts file uses optOutFromShadowRoot but the js code uses useShadowDom which means the type name is inverted

    export const create: (app: JSX.Element, tagName: string, optOutFromShadowRoot?: boolean) => void;

  2. the example in the readme is inverted (should be false but is true)

    ReactWebComponent.create(<App />, 'my-component', true);

  3. it might make more sense to pass in an options object to create instead of a single boolean flag so that it is clearer what true or false is doing at the callpoint

    e.g.

    ReactWebComponent.create(<App />, 'my-component', {useShadowDom: false});

    instead of

    ReactWebComponent.create(<App />, 'my-component', false);

Web component attributes not getting passed down

How are attributes envisioned to be passed to the react component?
Are you perhaps using events to pass data to your web component rather than via event or attributes?

For example I could use lifecycle event webComponentConstructed and get the web component as a dom element there, and then do something like this:

webComponentConstructed(el) {
  console.log(el.attributes); // Returns an array of attributes
}

But could this be passed to react components props instead? I think it would make for a nicer integration.

Do you think this makes sense?

Passing props in html page

Hello,

I confirm that i have the same issue.
If I pass prop (ie name="Bob") in the html page,
this.props is empty ....

I am using Google Chrome on Windows

Regards

How to switch off Shadow dom?

I'm trying to re-use styles from the parent component.
Currently I believe that the shadow dom is "turned on" and prevents such a thing.

image

Web components v1 and more

Hey! Love this library..

I wanted to make it work with the v1 version of web components since that's what will be supported in the future. Actually I wanted to make a pull request, but then I saw there was a rewrite on the way already.

Anyway, I made a gist that works with web components v1 that also has a fallback to work without shadow dom (even without polyfilling it):

https://gist.github.com/magnusjt/32250b89bbc3a529c413f1fc8cc256f8

If this is interesting, I can make a pull request out of it.

Doesn't work with React.js 17

I am using CRA 5.0.0, and try to install this npm-module. Then I got this error message:

PS C:\temp\temp\helloworld> npm install react-web-component
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR!
npm ERR! While resolving: [email protected]
npm ERR! Found: [email protected]
npm ERR! node_modules/react
npm ERR! react@"^17.0.2" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer react@"^16.2.0" from [email protected]
npm ERR! node_modules/react-web-component
npm ERR! react-web-component@"*" from the root project
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force, or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR!

bug: the appInstance variable is shared by all the instances of the web component

Hello. The appInstance variable is shared by all the instances of the web component in a manner that is subject to race conditions. i recommend storing it as an instance property on the class instead. This bug exhibits when multiple instances will contend for that variable, and then they'll end up dispatching, say webComponentAttributeChanged, to the wrong instance. I hope someone finds this insight helpful! Sorry I didn't have time to make a PR. Thanks!

React material ui doesn't function properly while having shadow dom enabled

Hi,
I have tested this issue using various react and material ui versions, apparently the issue happens to be quite widespread!
Once I enable shadow dom, I run into trouble having material ui styles displayed however everything is working fine as soon as shadow dom is disabled like the below command

ReactWebComponent.create(<App />, 'my-component', false);

could you point me toward the problem, I may be able to solve it and create PR as well on my own!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.