大家好,我是 CUGGZ。之前分享過(guò)一篇《使用 TypeScript 編寫(xiě) React 的最佳實(shí)踐!》,文中介紹了 React 和 TypeScript 搭配使用的一些常見(jiàn)用法。其中第四部分介紹了在React的事件處理中如何定義事件類(lèi)型,下面來(lái)通過(guò)一些簡(jiǎn)單的 Demo (每個(gè) Demo 后面都有 CodeSandBox 的在線(xiàn)體驗(yàn)地址)看看如何在 React + TypeScrip 中處理常見(jiàn)的事件!
目錄:
onClickonChangeonScrollonSubmitonCopy、onCut、onPasteonMouseOver、onMouseOutonLoad、onErroronkeydown、onkeypress、onkeyuponFocus、onBluronDragStart、onDrop、onDragOverwindow.resize1. onClickonClick是用的最多的事件之一,這里主要列舉兩種類(lèi)型的onClick事件:
button按鈕的onClick事件;任意元素的的onClick事件。下面先來(lái)看看按鈕的onClick事件,當(dāng)點(diǎn)擊按鈕時(shí),在頁(yè)面顯示按鈕的名稱(chēng):
import React, { useState } from "react";import "./styles.css";const App: React.FunctionComponent = () => { const [clickedButton, setClickedButton] = useState(""); const buttonHandler = (event: React.MouseEvent) => { event.preventDefault(); const button: HTMLButtonElement = event.currentTarget; setClickedButton(button.name); }; return ( {clickedButton !== "" ? `點(diǎn)擊了 ${clickedButton}` : "沒(méi)有點(diǎn)擊任何按鈕"}
);};export default App;
可以看到,onClick 事件的事件處理對(duì)象的類(lèi)型都定義為了MouseEvent,其中傳入的參數(shù)為綁定事件的元素的類(lèi)型??梢酝ㄟ^(guò)事件對(duì)象的currentTarget屬性來(lái)獲取點(diǎn)擊元素的屬性。
在線(xiàn)體驗(yàn):https://codesandbox.io/s/dawn-feather-8gofq1
再來(lái)看看任意元素的 onClick事件,點(diǎn)擊一個(gè)元素時(shí),在控制臺(tái)打印點(diǎn)擊元素的類(lèi)型、長(zhǎng)度、寬度:
import React from "react";import "./styles.css";const App: React.FunctionComponent = () => { // 當(dāng) container 被點(diǎn)擊時(shí),觸發(fā)該事件 const divClickedHandler = (event: React.MouseEvent) => { const div = event.currentTarget; console.log( "ElementName: ", div.tagName, "Width: ", div.clientWidth, "Height: ", div.clientHeight ); }; // 當(dāng) h1 被點(diǎn)擊時(shí),觸發(fā)該事件 const headingClickedHandler = (event: React.MouseEvent) => { event.stopPropagation(); const heading = event.currentTarget; console.log( "ElementName: ", heading.tagName, "Width: ", heading.clientWidth, "Height: ", heading.clientHeight ); }; // 當(dāng)圖片被點(diǎn)擊時(shí),觸發(fā)該事件 const imgClickedHandler = (event: React.MouseEvent) => { event.stopPropagation(); const img = event.currentTarget; console.log( "ElementName: ", img.tagName, "Width: ", img.clientWidth, "Height: ", img.clientHeight ); }; return ( Hello World
);};export default App;
可以看到,onClick 事件的事件處理對(duì)象的類(lèi)型都定義為了MouseEvent,其中傳入的參數(shù)為綁定事件的元素的類(lèi)型。需要注意,在任意元素上添加點(diǎn)擊事件時(shí),會(huì)觸發(fā)事件冒泡,比如上面的例子,當(dāng)點(diǎn)擊是圖片或者h(yuǎn)1標(biāo)簽時(shí)就會(huì)導(dǎo)致其父元素div的點(diǎn)擊事件觸發(fā)??梢允褂孟旅娴拇a來(lái)避免默認(rèn)事件:
event.stopPropagation();
在線(xiàn)體驗(yàn):https://codesandbox.io/s/serverless-glade-g41upi
2. onChange下面來(lái)看看onChange事件,先來(lái)看select元素的onChange事件的例子,當(dāng)選中元素時(shí),選中元素的值會(huì)顯示在頁(yè)面上:
import React, { useState } from "react";import "./styles.css";const App: React.FunctionComponent = () => { const [selectedOption, setSelectedOption] = useState(); const selectChange = (event: React.ChangeEvent) => { const value = event.target.value; setSelectedOption(value); }; return ( {selectedOption && {selectedOption}
} );};export default App;
可以看到,select元素的onSelect的事件對(duì)象類(lèi)型為ChangeEvent,傳入的參數(shù)為select元素的類(lèi)型??梢酝ㄟ^(guò)target屬性來(lái)獲取select選中的值。
在線(xiàn)體驗(yàn):https://codesandbox.io/s/frosty-lichterman-33fpky
input元素的onChange事件的例子,在輸入框中輸入內(nèi)容,點(diǎn)擊搜索按鈕,在頁(yè)面顯示搜索結(jié)果:
import React, { useState } from "react";import "./styles.css";interface Item { id: number; name: string; price: number;}const PRODUCTS: Item[] = [ { id: 1, name: "Apple", price: 1 }, { id: 2, name: "Book", price: 5 }, { id: 3, name: "Banana", price: 0.5 }, { id: 4, name: "Table", price: 200 }];const App: React.FunctionComponent = () => { const [query, setQuery] = useState(""); const [result, setResult] = useState- (); // 當(dāng) input 的內(nèi)容改變時(shí)觸發(fā) const inputHandler = (event: React.ChangeEvent
) => { const enteredName = event.target.value; setQuery(enteredName); }; // 點(diǎn)擊搜索時(shí)觸發(fā) const search = () => { const foundItems = PRODUCTS.filter((item) => item.name.toLowerCase().includes(query.toLowerCase()) ); setResult(foundItems); }; return ( {result && result.length > 0 ? ( result.map((item) => ( {item.id} {item.name} {item.price}¥ )) ) : ( 沒(méi)有找到!
)} );};export default App;
可以看到,這里input 的事件處理對(duì)象的類(lèi)型為ChangeEvent。要想獲取輸入的值需要從事件對(duì)象的 target 屬性中獲取。
在線(xiàn)體驗(yàn):https://codesandbox.io/s/pedantic-murdock-lejmg6
3. onScrollonScroll事件在元素的滾動(dòng)條被滾動(dòng)時(shí)觸發(fā)。
下面來(lái)看一個(gè)例子,當(dāng)元素發(fā)生滾動(dòng)時(shí),計(jì)算滾動(dòng)了多少的元素,從而計(jì)算頁(yè)面滾動(dòng)進(jìn)度的百分比值,并顯示在頁(yè)面上:
import React, { useState } from "react";import "./styles.css";const DUMMY_DATA = Array.from({ length: 100 }, (x, i) => { return { id: i, title: `Item ${i}` };});const App: React.FunctionComponent = () => { const [progress, setProgress] = useState(0); // 當(dāng)元素發(fā)生滾動(dòng)時(shí)觸發(fā)該事件 const scrollHandler = (event: React.UIEvent) => { const containerHeight = event.currentTarget.clientHeight; const scrollHeight = event.currentTarget.scrollHeight; const scrollTop = event.currentTarget.scrollTop; setProgress(((scrollTop + containerHeight) / scrollHeight) * 100); }; return ( <> {DUMMY_DATA.map((item) => ( {item.title} ))} {progress.toFixed(2)}%
> );};export default App;
可以看到,onScroll事件的事件對(duì)象類(lèi)型定義為了:React.UIEvent
在線(xiàn)體驗(yàn):https://codesandbox.io/s/competent-hellman-qh7non
4. onSubmit下面來(lái)看看表單的onSubmit事件,該事件在表單提交時(shí)觸發(fā):
import React, { useState } from "react";import "./styles.css";const App: React.FunctionComponent = () => { const [term, setTerm] = useState(""); const submitForm = (event: React.FormEvent) => { // 防止頁(yè)面重新加載 event.preventDefault(); alert(term); }; return ( );};export default App;
表單提交事件的時(shí)間對(duì)象類(lèi)型為FormEvent。需要注意,為了防止頁(yè)面在表單的onSubmit事件觸發(fā)時(shí)重新加載,需要調(diào)用:
event.preventDefault();
在線(xiàn)體驗(yàn):https://codesandbox.io/s/condescending-danny-e1eerd
5. onCopy、onCut、onPaste下面來(lái)看看常見(jiàn)的復(fù)制、剪切、粘貼這三個(gè)時(shí)間:
onCopy:在用戶(hù)復(fù)制元素或元素的內(nèi)容(如文本、圖像)時(shí)觸發(fā);onPaste:在用戶(hù)在元素中粘貼一些內(nèi)容時(shí)觸發(fā);onCut:在用戶(hù)剪切元素的內(nèi)容時(shí)發(fā)生,此事件主要用于input(`type=”text”``) 和 textarea 元素。下面來(lái)看一個(gè)例子,當(dāng)進(jìn)行復(fù)制、剪切、粘貼時(shí),給操作的元素加上一些樣式:
import React, { useState } from "react";import "./styles.css";const App: React.FunctionComponent = () => { const [text, setText] = useState("hello world"); // 復(fù)制:onCopy const copyHandler = (event: React.ClipboardEvent) => { event.currentTarget.style.border = "3px solid green"; }; // 剪切:onCut const cutHandler = (event: React.ClipboardEvent) => { event.currentTarget.style.border = "3px solid orange"; event.currentTarget.style.backgroundColor = "yellow"; event.currentTarget.disabled = true; setText("內(nèi)容被剪切啦"); }; // 粘貼:onPaste const pasteHandler = (event: React.ClipboardEvent) => { event.currentTarget.style.border = "5px solid purple"; event.currentTarget.style.backgroundColor = "orange"; event.currentTarget.value = event.clipboardData.getData("text").toUpperCase(); event.preventDefault(); }; return (
在下方粘貼:
);};export default App;
可以看到,這三個(gè)事件的事件處理對(duì)象的類(lèi)型都定義為了ClipboardEvent,其中傳入的參數(shù)為綁定事件的元素的類(lèi)型??梢酝ㄟ^(guò)currentTarget屬性來(lái)獲取事件對(duì)象的屬性。
在線(xiàn)體驗(yàn):https://codesandbox.io/s/sleepy-keldysh-w5vemj
6. onMouseOver、onMouseOutonmouseover和onmouseout是常用的兩個(gè)鼠標(biāo)事件:
onmouseover:在鼠標(biāo)指針移動(dòng)到指定的對(duì)象上時(shí)觸發(fā);onmouseout:在鼠標(biāo)指針移出指定的對(duì)象時(shí)觸發(fā)。下面來(lái)看一個(gè)例子,當(dāng)鼠標(biāo)在元素上和移出元素時(shí)給元素添加不同的樣式:
import React from "react";import "./styles.css";const App: React.FunctionComponent = () => { // 當(dāng)鼠標(biāo)指針位于box上時(shí),將觸發(fā)此功能 const boxMouseOverHandler = (event: React.MouseEvent) => { const box: HTMLDivElement = event.currentTarget; box.style.backgroundColor = "lightblue"; }; // 當(dāng)鼠標(biāo)指針移出box時(shí),將觸發(fā)此功能 const boxMouseOutHandler = (event: React.MouseEvent) => { const box: HTMLDivElement = event.currentTarget; box.style.backgroundColor = "lightgreen"; }; // 當(dāng)鼠標(biāo)指針位于輸入框上時(shí),將觸發(fā)此功能 const inputMouseOverHandler = (event: React.MouseEvent) => { const input: HTMLInputElement = event.currentTarget; input.style.backgroundColor = "lime"; }; //當(dāng)鼠標(biāo)指針移出輸入框時(shí),將觸發(fā)此功能 const inputMouseOutHandler = (event: React.MouseEvent) => { const input: HTMLInputElement = event.currentTarget; input.style.backgroundColor = "white"; }; //當(dāng)鼠標(biāo)指針位于按鈕上時(shí),將觸發(fā)此功能 const buttonMouseOverHandler = (event: React.MouseEvent) => { const btn: HTMLButtonElement = event.currentTarget; btn.style.border = "3px solid red"; btn.style.backgroundColor = "orange"; }; // 當(dāng)鼠標(biāo)指針移出按鈕時(shí),將觸發(fā)此功能 const buttonMouseOutHandler = (event: React.MouseEvent) => { const btn: HTMLButtonElement = event.currentTarget; btn.style.border = "none"; btn.style.backgroundColor = "yellow"; }; return ( );};export default App;
可以看到,這兩個(gè)事件的事件處理對(duì)象的類(lèi)型都定義為了MouseEvent,其中傳入的參數(shù)為綁定事件的元素的類(lèi)型??梢酝ㄟ^(guò)事件對(duì)象的currentTarget來(lái)獲取事件對(duì)象的屬性。
在線(xiàn)體驗(yàn):https://codesandbox.io/s/nervous-cloud-5r6d6p
7. onLoad、onErroronLoad和onError是頁(yè)面外部資源加載相關(guān)的兩個(gè)相關(guān)事件:
onload:資源加載失??;onerror:資源加載出錯(cuò)。下面來(lái)看一個(gè)例子, 當(dāng)圖片成功時(shí)給它添加類(lèi)名 success,加載失敗時(shí)添加類(lèi)型 error,并更換為備用圖片的URL:
import React from "react";import "./styles.css";const IMAGE ="https://resource-1255585089.cos.ap-beijing.myqcloud.com/111.png";const FALLBACK_IMAGE ="https://resource-1255585089.cos.ap-beijing.myqcloud.com/222.png";const App: React.FunctionComponent = () => { const imageOnLoadHandler = (event: React.SyntheticEvent) => { // 圖片加載成功時(shí),打印圖片的地址,并添加類(lèi)名 success console.log(event.currentTarget.src); if (event.currentTarget.className !== "error") { event.currentTarget.className = "success"; } }; const imageOnErrorHandler = (event: React.SyntheticEvent) => { // 圖片加載失敗時(shí),加載替代的圖片,并添加類(lèi)名 error event.currentTarget.src = FALLBACK_IMAGE; event.currentTarget.className = "error"; }; return (
);};export default App;
可以看到,這兩個(gè)事件的事件處理對(duì)象的類(lèi)型都定義為了SyntheticEvent,其中傳入的第一個(gè)參數(shù)為綁定事件的元素的類(lèi)型。可以通過(guò)事件對(duì)象的currentTarget屬性來(lái)獲取事件對(duì)象的屬性。
在線(xiàn)體驗(yàn):https://codesandbox.io/s/determined-tamas-rjwjoq
8. onkeydown、onkeypress、onkeyup下面來(lái)看幾個(gè)常見(jiàn)的鍵盤(pán)事件:
onKeyDown:在用戶(hù)按下一個(gè)鍵盤(pán)按鍵時(shí)觸發(fā);onKeyUp:在鍵盤(pán)按鍵被松開(kāi)時(shí)觸發(fā);onKeyPress:在鍵盤(pán)按鍵被按下并釋放一個(gè)鍵時(shí)發(fā)生。在所有瀏覽器中onkeypress事件只能監(jiān)聽(tīng)字母和數(shù)字,不能監(jiān)聽(tīng)一些特殊按鍵(ALT、CTRL、SHIFT、ESC、箭頭等)。監(jiān)聽(tīng)一個(gè)用戶(hù)是否按下按鍵請(qǐng)使用onkeydown事件,所有瀏覽器都支持onkeydown事件。這三個(gè)事件的執(zhí)行順序如下:
onkeydownonkeypressonkeyup來(lái)看一個(gè)例子,按下ESC鍵可以清除已經(jīng)輸入的文本,按下Enter鍵可以彈出已經(jīng)輸入的文本:
import React, { useState } from "react";import "./styles.css";const App: React.FunctionComponent = () => { const [enteredText, setEnteredText] = useState(""); // onKeyDown 事件處理函數(shù) const keyDownHandler = (event: React.KeyboardEvent) => { if (event.code === "Enter") { alert(`輸入內(nèi)容:"${enteredText}"`); } }; // onKeyUp 事件處理函數(shù) const keyUpHandler = (event: React.KeyboardEvent) => { if (event.code === "Escape") { const confirm = window.confirm("確定清除文本嗎?"); if (confirm) { setEnteredText(""); } } }; // onKeyPress 事件處理函數(shù) const keyPressHandler = (event: React.KeyboardEvent) => { //... }; return ( setEnteredText(e.target.value)} /> );};export default App;
這三個(gè)事件的事件對(duì)象類(lèi)型都是KeyboardEvent??梢酝ㄟ^(guò)事件對(duì)象的code屬性獲取按下的鍵盤(pán)鍵值。
在線(xiàn)體驗(yàn):https://codesandbox.io/s/prod-sky-txwzgd
再來(lái)看一個(gè)簡(jiǎn)單的例子,通過(guò)在鍵盤(pán)上按下上下左右鍵使得盒子在頁(yè)面上移動(dòng):
import React, { useState } from "react";import "./styles.css";const App: React.FunctionComponent = () => { const [left, setLeft] = useState(0); const [top, setTop] = useState(0); // onKeyDown 事件處理函數(shù) const keyDownHandler = (event: React.KeyboardEvent) => { console.log(event.code); if (event.code === "ArrowUp") { setTop((top) => top - 10); } if (event.code === "ArrowDown") { setTop((top) => top + 10); } if (event.code === "ArrowLeft") { setLeft((left) => left - 10); } if (event.code === "ArrowRight") { setLeft((left) => left + 10); } }; return ( );};export default App;
在線(xiàn)體驗(yàn):https://codesandbox.io/s/hungry-meninsky-zhkbzb
9. onFocus、onBluronfocus:在元素獲得焦點(diǎn)時(shí)觸發(fā),適用于、