I followed React's documentation on how to use React inside of a Web Component (https://reactjs.org/docs/web-components.html) but found that the synthetic event system just does not work for the React tree inside of that React component.
Here is a JS Fiddle where you can try:
- A React component embedded naturally without Web Components
- The same React component embedded inside of a Web Component (events won't fire)
- Another React component that attaches event listeners manually, inside a Web Component
class CounterWithSyntheticEvents extends React.Component {
constructor(props) {
super(props);
this.state = {
counter: 0,
};
}
handleClick() {
this.setState(prevState => ({ counter: prevState.counter + 1 }));
}
render() {
return (
<div>
Count {this.state.counter} <button onClick={this.handleClick.bind(this)}>+</button>
</div>
);
}
}
class CounterWithDOMEvents extends React.Component {
constructor(props) {
super(props);
this.buttonRef = React.createRef();
this.state = {
counter: 0,
};
}
componentDidMount() {
const listenerFn = this.handleClick.bind(this);
this.buttonRef.current.addEventListener("click", listenerFn);
this.removeListener = () => this.buttonRef.current.removeEventListener('click', listenerFn);
}
componentWillUnmount() {
this.removeListener();
}
handleClick() {
this.setState(prevState => ({ counter: prevState.counter + 1 }));
}
render() {
return (
<div>
Count {this.state.counter} <button ref={this.buttonRef}>+</button>
</div>
);
}
}
ReactDOM.render(<CounterWithSyntheticEvents />, document.getElementById("container"));
class ReactSyntheticEvents extends HTMLElement {
connectedCallback() {
const mountPoint = document.createElement("div");
const shadow = this.attachShadow({ mode: "open" });
shadow.appendChild(mountPoint);
ReactDOM.render(<CounterWithSyntheticEvents />, mountPoint);
}
}
customElements.define("react-synthetic-events", ReactSyntheticEvents);
class ReactDOMEvents extends HTMLElement {
connectedCallback() {
const mountPoint = document.createElement("div");
const shadow = this.attachShadow({ mode: "open" });
shadow.appendChild(mountPoint);
ReactDOM.render(<CounterWithDOMEvents />, mountPoint);
}
}
customElements.define("react-dom-events", ReactDOMEvents);
<div id="container"></div>
<react-synthetic-events ></react-synthetic-events>
<react-dom-events ></react-dom-events>
The approach with the manual listeners binding works, but I want to avoid rewriting every single react component that is to be integrated.
Does anyone know about a quick fix I could do in order to have events propagated to the components ? (to make the 2nd case of the fiddle work)
I have heard of Polymer-react but haven't given it a try yet. And I'd like to not introduce another layer if possible.