DEV Community

Koutaro Chikuba
Koutaro Chikuba

Posted on

Mount vue component in react with webcomponents

I tried helpers to mount react and vue components via webcomponents.

https://github.com/mizchi/wc-helpers

My goal is to try micro-frontend.

import {
  vueElementFactory,
  reactElementFactory,
  encodeProps
} from "@mizchi/wc-helpers";

// register
customElements.define("my-react-component", reactElementFactory(MyReactApp));
customElements.define("my-vue-component", vueElementFactory(MyVueApp));

// run
const state = { a: 0 };
const encoded = encodeProps(state);
document.body.innerHTML = `
  <my-react-component data-props="${encoded}"></my-react-component>
  <my-vue-component data-props="${encoded}"></my-vue-component>
`;

Mount another platform

  • my-react-component(depth=n) => my-vue-component(depth=n-1)
  • my-vue-component(depth=n) => my-react-component(depth=n-1)

HTML

Code

import Vue from "vue";
import Component from "vue-class-component";
import React from "react";
import { vueElementFactory, reactElementFactory, encodeProps } from "..";

// Vue
@Component({
  props: ["depth"]
})
class MyVueApp extends Vue {
  now: number = Date.now();
  render(h: any) {
    const depth = parseInt(this.$props.depth, 10);
    if (depth > 0) {
      return h(
        "div",
        {
          style: {
            paddingLeft: "10px",
            backgroundColor: "rgba(0, 255, 0, 0.3)"
          }
        },
        [
          h("p", {}, `vue: ${depth} createdAt:${this.now}`),
          h("my-react-component", {
            attrs: {
              "data-props": encodeProps({ depth: depth - 1 })
            }
          })
        ]
      );
    } else {
      return h(
        "p",
        {
          style: {
            paddingLeft: "10px",
            backgroundColor: "rgba(0, 255, 0, 0.3)"
          }
        },
        `vue: 0 - createtAt:${this.now}`
      );
    }
  }
}
customElements.define("my-vue-component", vueElementFactory(MyVueApp));

// React
class MyReactApp extends React.PureComponent<{ depth: string }> {
  now: number = Date.now();
  render() {
    const h = React.createElement;
    const depth = parseInt(this.props.depth, 10);
    if (depth > 0) {
      return h(
        "div",
        {
          style: {
            paddingLeft: "10px",
            backgroundColor: "rgba(255, 0, 0, 0.3)"
          }
        },
        h("p", {}, `react: ${depth} createdAt: ${this.now}`),
        h("my-vue-component", {
          "data-props": encodeProps({ depth: depth - 1 })
        })
      );
    } else {
      return h(
        "p",
        {
          style: {
            paddingLeft: "10px",
            backgroundColor: "rgba(255, 0, 0, 0.3)"
          }
        },
        "react 0: createdAt " + this.now
      );
    }
  }
}
customElements.define("my-react-component", reactElementFactory(MyReactApp));

// run
// const root = document.querySelector(".root");

let state = { depth: 4 };
const entry = document.createElement("my-react-component");
entry.setAttribute("data-props", state.depth.toString());
document.body.appendChild(entry);

setInterval(() => {
  state = { depth: state.depth + 1 };
  entry.setAttribute(
    "data-props",
    encodeProps({ depth: (state.depth % 6) + 3 })
  );
}, 1000);

How it works

  • Encode json to safe string
  • Define Component
  • Listen data-props's attributeChangedCallback
  • re-render

See implementation (114 lines) https://github.com/mizchi/wc-helpers/blob/master/index.ts

Why wc-helper needs encode

WebComponents takes properties as string only. If you need to handle event handlers, try lit-html and lit-html/lib/lit-extended.

https://github.com/Polymer/lit-html

Top comments (0)