Catopus
Catopus

Reputation: 101

Event type for event listener callback function - React & Typescript

I'm trying to handle scroll event so a new class name can be added to an element upon scrolling.

What I have done:

Here's the code:

  const onScrollHandler = (e: React.ChangeEvent):void => {
    if (!e.target.classList.contains('onScrollBar')) {
      e.target.classList.add('onScrollBar');
    }
  };

  useEffect(() => {
    window.addEventListener('scroll', onScrollHandler, true);

    return () => {
      window.removeEventListener('scroll', onScrollHandler, true);
    };
  }, []);

The issue I got:

No overload matches this call.
  Overload 1 of 2, '(type: "scroll", listener: (this: Window, ev: Event) => any, options?: boolean | AddEventListenerOptions | undefined): void', gave the following error.
    Argument of type '(e: React.ChangeEvent) => void' is not assignable to parameter of type '(this: Window, ev: Event) => any'.
      Types of parameters 'e' and 'ev' are incompatible.
        Type 'Event' is missing the following properties from type 'ChangeEvent<Element>': nativeEvent, isDefaultPrevented, isPropagationStopped, persist
  Overload 2 of 2, '(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions | undefined): void', gave the following error.
    Argument of type '(e: React.ChangeEvent) => void' is not assignable to parameter of type 'EventListenerOrEventListenerObject'.
      Type '(e: React.ChangeEvent) => void' is not assignable to type 'EventListener'.
        Types of parameters 'e' and 'evt' are incompatible.
          Type 'Event' is not assignable to type 'ChangeEvent<Element>'.ts(2769)

I think this is something to do with the e: React.ChangeEvent. So what should be the correct event type in this case?

Many thanks!

Upvotes: 2

Views: 10207

Answers (2)

Vector-Hector
Vector-Hector

Reputation: 350

First, you need to use document to add the listener, or that scoll listener will not do anything.

It seems React.ChangeEvent is only applicable for <select>, <input> or <textarea> onChange events. So you need to use the type Event for scroll events and such. Now, the scroll target is most of the time the DOM, which does not have a classList. To get the proper type, I used the as keyword and checked if it has a classList.

Here's my full code:

const onScrollHandler = (e: Event) => {
    if (!e.target)
        return

    const target = e.target as Element
    if (target.classList === undefined) // ensure it's not the DOM
        return

    if (!target.classList.contains('onScrollBar')) 
        target.classList.add('onScrollBar');
};
useEffect(() => {
    document.addEventListener('scroll', onScrollHandler, true);
    return () => {
        document.removeEventListener('scroll', onScrollHandler, true);
    };
}, []);

Upvotes: 3

Catopus
Catopus

Reputation: 101

My colleague has helped me with this solution and it works wonders for me :D

So basically, the event type should be changed as Event and when calling e.target, we need to specify it as Element

Here's the fix:

  const onScrollHandler = (e: Event):void => {
    if((e.target as Element).classList.contains('onScrollBar')) {
      ((e.target as Element).classList.add('onScrollBar'))
    }
  };

  useEffect(() => {
    document.addEventListener('scroll', onScrollHandler, true);

    return () => {
      document.removeEventListener('scroll', onScrollHandler, true);
    };
  }, []);

Upvotes: 1

Related Questions