I would like to integrate web components into my react app. They define a set of custom events that the enclosing react app must listen to.
According to the react docs:
Events emitted by a Web Component may not properly propagate through a React render tree. You will need to manually attach event handlers to handle these events within your React components.
I've struggled to make this work. I can dispatch custom events from react components and listen to them on dom elements. But I can't catch these events on other react components.
How do I correctly listen to custom events (and ideally, also be able to dispatch them) from a react app?
Minimal example:
I've set up a minimal example (edit live on sandbox.io). It consists of two divs, the outer one listens to my custom event, the inner one hosts the virtual dom. In the virtual dom there are two components. The outer one listens to the custom event, the inner one dispatches it. When run, the outer div registers the custom event. Inside of the react app, it is not caught.
If you want to play around with the code, I've set it up as a repo:
git clone https://github.com/lhk/react_custom_events
cd react_custom_events
npm i
npm run start
# browser opens, look at the console output
index.html
, contains a div which will listen to custom dom elements, and inside of it is the root of the react app.
<div id='listener'>
<div id="react_root"></div>
</div>
The react app consists of two functional components: Listener and Dispatcher. index.tsx
renders the react app and sets up a listener on the dom div
:
document.getElementById("listener")?.addEventListener("custom", ev => {
console.log("dom received custom event");
});
ReactDOM.render(<Listener />, document.getElementById("react_root"));
And here are the two components, they dispatch and listen to the custom dom event.
Listener.tsx
:
import React, { useEffect, useRef } from "react";
import Dispatcher from "./Dispatcher";
export default function Listener() {
const divRef = useRef(null);
useEffect(() => {
(divRef.current as any).addEventListener("custom", (ev:any) => {
console.log("react received custom event");
});
});
return (
<div ref={divRef}>
<Dispatcher />
</div>
);
}
Dispatcher.tsx
:
import React, { useEffect, useRef } from "react";
import { customEvent } from "./events";
export default function Dispatcher() {
const pRef = useRef(null);
useEffect(() => {
(pRef.current as any).dispatchEvent(customEvent);
});
return (
<div>
<p ref={pRef}>Some Text</p>
</div>
);
}
Finally, the custom event is declared like this:
export var customEvent = new Event('custom', { bubbles: true });
Related questions:
This question sounds very similar: Listening for web component events in React
But it's not really about the system of custom events in react. Instead it asks on how to expose an event attribute on a polymer component.
This question is also about custom events, but not in the context of react: How to listen for custom events defined web component
This question seems to be just a syntax error: addEventListener in a React app isn't working
We can create custom events using the Event constructor. We can now finally create our non-existent “ onDialogClose ” event as such: //First, we initialize our event const event = new Event('onDialogClose'); // Next, we dispatch the event. elem.
To listen to events in React, add the onClick attribute — which is the event handler — to the target element.
When using React, you generally don't need to call addEventListener to add listeners to a DOM element after it is created. Instead, just provide a listener when the element is initially rendered. You have to be careful about the meaning of this in JSX callbacks. In JavaScript, class methods are not bound by default.
The dispatchEvent() method of the EventTarget sends an Event to the object, (synchronously) invoking the affected EventListener s in the appropriate order. The normal event processing rules (including the capturing and optional bubbling phase) also apply to events dispatched manually with dispatchEvent() .
It seems to me that this is simply not possible in react.
React offers a system of synthetic events. This has been implemented for compatibility reasons, the synthetic events expose the same API across browsers. Internally, react wraps native DOM events into synthetic events.
However, these synthetic events also introduce a series of limitations:
It is possible to listen to custom events in the virtual dom, but only on the same element that dispatches them. The following code will catch the event:
export default function Dispatcher() {
const pRef = useRef(null);
useEffect(() => {
(pRef.current as any).addEventListener('custom', ()=>{
console.log('react caught custom event directly on component');
});
(pRef.current as any).dispatchEvent(customEvent);
});
return (
<div>
<p ref={pRef}>Some Text</p>
</div>
);
}
I would argue that this makes custom events pretty much useless. If you have to get a reference to the specific dom node that triggered the custom event, you might as well give it a callback.
If you are interested in reading up on this, there are a variety of github issues. Unfortunately the facebook react team seems to moderate them quite heavily. They are simply closed without further comment :(
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With