繁体   English   中英

React + TypeScript:滚动事件类型不可分配给 window.addEventListener

[英]React + TypeScript: Scroll event type not assignable to window.addEventListener

是不是 TypeScript DOM 类型的事件和反应事件类型不 go 与另一个很好?

看看这段代码:

  useEffect(() => {
    const onScroll = (event: React.ChangeEvent<Body>) => {
      console.log("event", event.target);
    };

    window.addEventListener("scroll", onScroll);

    return () => window.removeEventListener("scroll", onScroll);
  }, []);

它抛出

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 '(event: ChangeEvent<Body>) => void' is not assignable to parameter of type '(this: Window, ev: Event) => any'.
      Types of parameters 'event' and 'ev' are incompatible.
        Type 'Event' is not assignable to type 'ChangeEvent<Body>'.

比我尝试了以下。 这编译正确。

  useEffect(() => {
    const onScroll = (event: Event) => { // <--- changed event type here
      console.log("event", event.target);
    };

    window.addEventListener("scroll", onScroll);

    return () => window.removeEventListener("scroll", onScroll);
  }, []);

但是,如果没有编译错误,则无法访问event.target 我检查了以下 function 调用(如果我忽略错误,它在浏览器中工作):

  useEffect(() => {
    const onScroll = (event: Event) => {
      console.log(
        "event",
        event?.target?.activeElement.getBoundingClientRect() // <--- this function call is actually working (ts-ignored it), even if TypeScript throws an error
      );
    };

    window.addEventListener("scroll", onScroll);

    return () => window.removeEventListener("scroll", onScroll);
  }, []);

投掷

TypeScript error in /home/sirhennihau/Workspace/giphy-search/src/Components/grid.tsx(27,24):
Property 'activeElement' does not exist on type 'EventTarget'.  TS2339

    25 |       console.log(
    26 |         "event",
  > 27 |         event?.target?.activeElement.getBoundingClientRect()
       |                        ^
    28 |       );
    29 |     };
    30 |

我的依赖:

    "@types/node": "^12.0.0",
    "@types/react": "^16.9.53",
    "@types/react-dom": "^17.0.0",
    "react": "^17.0.1",
    "react-dom": "^17.0.1",
    "react-scripts": "4.0.1",
    "typescript": "^4.0.3"

也许只是打字不好还是我做错了什么?

好吧,是的,React-Events 和 DOM-Events有时不能相互“顺利”,但是有一些干净的方法可以处理您的用例(根据我的经验,React 中的大多数用例):

DOM 事件

DOM 方法window.addEventListener需要一个 DOM- EventListener ( (evt: Event): void; ),因此您需要使用 DOM- Event接口(就像您所做的那样)。

在这里,我在您的代码中添加了一些类型来说明这一点:

useEffect(() => {
    const onScroll: EventListener = (event: Event) => { // <-- DOM-EventListener
        console.log("event", event.target);
    };

    const win: Window = window;   // <-- DOM-Window, extends DOM-EventTarget
    win.addEventListener("scroll", onScroll);

    return () => window.removeEventListener("scroll", onScroll);
}, []);

event.target

event.target应该是可访问的。 我假设您的意思是如果您尝试访问event.target.activeElement会出错。

event.target.activeElement会抛出错误,因为Event可以是任何事件, event.target可以是扩展EventTarget | null的任何元素 EventTarget | null ,它不是特定元素,可能没有activeElement

但尽管存在 TS 错误,它仍然有效,因为实际上它是一个具有.activeElement的元素。

const onScroll: EventListener = (event: Event) => {
    const targetAny: EventTarget | null = event.target; // <-- maybe has no .activeElement
    console.log( targetAny.activeElement );
};

如果您确定元素是什么,您可以键入断言特定元素:

const onScroll: EventListener = (event: Event) => {
    const targetDiv: HTMLDocument = event.target as HTMLDocument; // <-- assert DOM-HTMLDocument
    console.log("DOM event", targetDiv.activeElement);    // <-- activeElement works
};

反应事件

React-Types 应该与 React-Elements 一起使用。

仍然必须断言event.target的类型才能访问特定元素的属性(这里我使用.style ),因为.target (扩展React.BaseSyntheticEvent )的React.SyntheticEvent是完全相同的 DOM- Event使用的EventTarget

需要明确的是:是的,React 也使用了一些 DOM-Types,所以这里 React 与 DOM-Events “进展顺利”。

const onScrollReact: React.EventHandler<React.UIEvent<ReactNode>> = (event: React.UIEvent<React.ReactNode> ) => {
    const target: EventTarget = event.target;     // <-- React uses the DOM EventTarget
    const targetDiv: HTMLDivElement = target as HTMLDivElement; // <-- type assert DOM-HTMLDivElement
    console.log("React event", target.style);     // <-- throws TS error
    console.log("React event", targetDiv.style);  // <-- no TS error
};

return (<div
    onScroll={ onScrollReact }
    style={{ height: '400px', overflow: 'scroll' }}
>
    <div style={{ height: '2000px', backgroundColor: '#eee' }}>
        try scroll event
    </div>
</div>);

您也可以在本例中使用HTMLDivElement (行为完全相同,但我猜它更具“声明性”):

const onScrollReact2: React.EventHandler<React.UIEvent<HTMLDivElement>> = (event: React.UIEvent<HTMLDivElement>) => {
    console.log("React event", event.target.style); // <-- still throws TS error
};

这对我有用 -

//register the scroll event listener callback function
useEffect(() => {
    window.addEventListener('scroll', onScroll)
    return () => {
      window.removeEventListener('scroll', onScroll)
    }
  })
// scroll event callback function
  const onScroll = (e: Event) => {
    const window = e.currentTarget as Window
    //... rest of you function
  }

确保在tsconfig.json中包含 dom lib

"compilerOptions": {
      "target": "es5",
      "lib": [
        "dom",
        "dom.iterable",
        ...
      ],

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM