[英]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.