react-web-component icon indicating copy to clipboard operation
react-web-component copied to clipboard

Passing props in html page

Open artyprog opened this issue 6 years ago • 3 comments

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

artyprog avatar Oct 15 '18 08:10 artyprog

Darn, I was hoping it was just me, it quick look at the source code seems that it extracts props from appInstance.props = extractAttributes(webComponentInstance);

but if useShadowDom is true it reassigns webComponentInstance to shadowRoot. // Re-assign the webComponentInstance (this) to the newly created shadowRoot webComponentInstance = webComponentInstance.createShadowRoot();

So, as a workaround you can disable shadowRoot. ReactWebComponent.create(<App />, 'my-component', false);

I then ran into the issue that props didn't seem to be set on the first render as it seems the render happens before webComponentConstructed where they get set... so I came up with the following quick hack:

  webComponentConstructed(el) {
  // props is updated parsed by react-web-component just before invoking this method
  // but react doesn't detect this change to props so copy it to state and use state instead.
    this.setState(this.props); 
  }

kurtharriger avatar Oct 21 '18 20:10 kurtharriger

After digging deeper I decided to implement the following instead of using this library.
It doesn't have the style stuff and seems to work for me so far but your mileage may vary:

function defineElement(Component, elementName, observedAttributes) {
  class CustomElement extends HTMLElement {
    connectedCallback() {
      const customElement = this;
      const shadowRoot = this.attachShadow({mode: 'open'});
      const props = [...this.attributes].reduce((props, attribute) => ({...props, [attribute.name]: attribute.value }),
        {customElement, shadowRoot});

      const instance =(<Component {...(props)}/>);

      ReactDOM.render(instance, shadowRoot);

      this.instance = instance;
      this.props = props;
    }
    attributeChangedCallback(name, oldValue, newValue) {

      const { shadowRoot, instance, props } = this;
      if(!instance) return;

      const newProps = {...(this.props), ...({[name]: newValue})};
      const newInstance =(<Component {...(newProps)}/>);

      ReactDOM.render(newInstance, shadowRoot);

      this.instance = newInstance;
      this.props = newProps;
    }
  }
  CustomElement.observedAttributes = observedAttributes;

  window.customElements.define(elementName, CustomElement);
}

const HelloWorld = (props) => {
  const onclick = () => {
    props.customElement.dispatchEvent(new CustomEvent('die'));
  }

  return (<h1>{props.greet || "Hi"}, {props.name || "World"}!
    <button onClick={onclick}>Die!</button>
  </h1>);
}

defineElement(HelloWorld, 'hello-world', ['greet', 'name']);

const hello = document.createElement("hello-world");

hello.setAttribute("name", "Visitor");
hello.addEventListener("die",  () => {
  hello.setAttribute("greet", "Goodbye");
});
document.body.appendChild(hello);

kurtharriger avatar Oct 21 '18 23:10 kurtharriger

I think this is similar to the issue #7.

mnemanja avatar Jul 03 '19 06:07 mnemanja