Sunday, May 19, 2024
 Popular · Latest · Hot · Upcoming
153
rated 0 times [  160] [ 7]  / answers: 1 / hits: 18488  / 4 Years ago, mon, march 30, 2020, 12:00:00

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


More From » reactjs

 Answers
34

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:




  • synthetic events don't cover all native events. Most notably there are changes around onInput and onChange.

  • there are no synthetic versions of custom events

  • native (custom) events will bubble through the virtual dom, without triggering any event listeners. After leaving the virtual dom, they will continue to bubble through the remainder of the dom. This is what happens in my example code.

  • it is not possible to dispatch synthetic events yourself



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 :(




[#51092] Thursday, March 19, 2020, 4 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
joannam

Total Points: 331
Total Questions: 106
Total Answers: 105

Location: Sweden
Member since Mon, May 8, 2023
1 Year ago
;