簡體   English   中英

為什么事件處理程序被調用兩次?

[英]Why is the event handler called twice?

我正在開發一個應該在移動設備上運行的React JS應用程序。

以下是應用程序的代碼摘錄:

索引.tsx:

import React from 'react';
import ReactDOM from 'react-dom';

import './index.css';
import App from './App';


ReactDOM.render(
    <React.StrictMode>
        <App/>
    </React.StrictMode>,
    document.getElementById('root')
);

應用程序.tsx:

import React from 'react';
import './App.css';

import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';

import {Header} from "./Components/header"
import {Footer} from "./Components/footer"
import {Sidebar} from "./Components/sidebar"
//...

const listener = () => {
    console.log("Main listener")
};

function App() {

    const media = window.matchMedia('(min-width: 700px)');
    media.addEventListener("change", listener);

    return (
        <Router>
            <div className="App">

                <Header />
                <Sidebar />
                <Footer />

                //...

            </div>
        </Router>
    );
}

export default App;

問題是change事件處理函數( listener器)被調用了兩次。 當我調整瀏覽器窗口大小時。 我讀過調查,原因是React Strict Mode標簽。 正如官方文檔所說, Strict Mode會重復調用以下函數:

  • 類組件構造函數、render 和 shouldComponentUpdate 方法

  • 類組件靜態 getDerivedStateFromProps 方法

  • 函數組件體

  • 狀態更新函數(setState 的第一個參數)

  • 傳遞給 useState、useMemo 或 useReducer 的函數

我的問題是為什么綁定事件被稱為兩次,因為文檔沒有將事件處理程序列為被雙重調用?

它的上下文是什么最終使change事件被調用兩次?

這是 React 的StrictMode主動引起你注意的錯誤。 你不應該在組件函數的頂層訂閱事件,因為每次渲染組件時都會調用它,所以每次你添加一個新的事件訂閱。 當您對一個事件有多個訂閱(在不同的事件源對象上)時,您會收到多個對事件回調的回調(每個訂閱一個)。

相反,在具有空依賴項數組的useEffect回調中訂閱一次(並取消訂閱您從中返回的清理回調),詳細信息在這里

function App() {

    // An effect only called once on mount (empty deps array)
    useEffect(() => {
        // Subscribe once
        const media = window.matchMedia("(min-width: 700px)");
        media.addEventListener("change", listener);
        // Unsubscribe on unmount
        return () => {
            media.removeEventListener("change", listener);
        };
    }, []);

    // ...
}

在您提出的評論中:

...您能解釋一下為什么不進行以下顯式多事件訂閱嗎:

 media.addEventListener("change", listener); media.addEventListener("change", listener)

因為在該代碼中,您在同一個MediaQueryList上兩次調用addEventListener並傳入同一個事件處理程序 ( listener )。 如果將同一事件的同一偵聽器添加到同一對象兩次,則第二次將被忽略(由 DOM 事件系統處理;不同的系統處理方式不同)。

但這不是您的組件正在做的。 您的組件函數運行了不止一次,每次它都會創建一個MediaQueryList並向其添加一個處理程序,如下所示:

 const listener = () => { console.log("listener fired"); }; const YourComponent = () => { const media = window.matchMedia("(min-width: 700px)"); media.addEventListener("change", listener); }; YourComponent(); YourComponent();

或者更明確地說:

const media1 = window.matchMedia("(min-width: 700px)");
media1.addEventListener("change", listener);
const media2 = window.matchMedia("(min-width: 700px)");
media2.addEventListener("change", listener);

由於它們是不同的MediaQueryList對象,因此每次都會添加偵聽器(每個對象一次)。

如果您運行該代碼段然后更改瀏覽器大小以便觸發偵聽器,您會看到它觸發了兩次。

你的代碼沒有問題,這只是當你將你的反應更新為反應 18 時反應的默認行為。你可以通過禁用嚴格模式來禁用它,這是你如何做到的: https ://upmostly.com/tutorials /為什么我的useeffect-hook-running-in-react 兩次

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM