Cách xử lý sự kiện DOM và Window bằng React
Trong phát triển web, các sự kiện đại diện cho các hành động xảy ra trong trình duyệt web. Bằng cách phản hồi các sự kiện với trình xử lý sự kiện , bạn có thể tạo các ứng dụng JavaScript động phản hồi bất kỳ hành động nào của user , bao gồm nhấp bằng chuột, cuộn dọc trang web, chạm vào màn hình cảm ứng, v.v. Trong ứng dụng React , bạn có thể sử dụng trình xử lý sự kiện để cập nhật dữ liệu trạng thái , kích hoạt các thay đổi hỗ trợ hoặc ngăn các hành động mặc định của trình duyệt. Để làm điều này, React sử dụng một shell bọc SyntheticEvent
thay vì giao diện Event
root . SyntheticEvent
mô phỏng chặt chẽ sự kiện trình duyệt tiêu chuẩn, nhưng cung cấp hành vi nhất quán hơn cho các trình duyệt web khác nhau. React cũng cung cấp cho bạn các công cụ để thêm và xóa Window
nghe sự kiện Window
một cách an toàn khi một thành phần mount và ngắt kết nối khỏi Mô hình đối tượng tài liệu (DOM) , cho phép bạn kiểm soát các sự kiện của Window
đồng thời ngăn chặn rò rỉ bộ nhớ từ trình nghe bị xóa không đúng cách.
Trong hướng dẫn này, bạn sẽ học cách xử lý các sự kiện trong React. Bạn sẽ xây dựng một số thành phần mẫu xử lý các sự kiện của user , bao gồm thành phần đầu vào tự xác thực và chú giải công cụ thông tin cho biểu mẫu đầu vào. Trong suốt hướng dẫn, bạn sẽ học cách thêm trình xử lý sự kiện vào các thành phần, lấy thông tin từ SyntheticEvent
và thêm và xóa trình xử lý sự kiện Window
. Đến cuối hướng dẫn này, bạn có thể làm việc với nhiều trình xử lý sự kiện khác nhau và áp dụng danh mục các sự kiện được React hỗ trợ .
Yêu cầu
Bạn cần một môi trường phát triển chạy Node.js ; hướng dẫn này đã được thử nghiệm trên Node.js version 10.22.0 và npm version 6.14.6. Để cài đặt phần mềm này trên macOS hoặc Ubuntu 18.04, hãy làm theo các bước trong Cách cài đặt Node.js và Tạo Môi trường Phát triển Cục bộ trên macOS hoặc phần Cài đặt Sử dụng PPA của Cách Cài đặt Node.js trên Ubuntu 18.04 .
Môi trường phát triển React được cài đặt với Create React App , với phần trình bày không cần thiết bị xóa. Để cài đặt điều này, hãy làm theo Bước 1 - Tạo một dự án trống của hướng dẫn Cách quản lý trạng thái trên các thành phần lớp React . Hướng dẫn này sẽ sử dụng hướng dẫn
events-tutorial
làm tên dự án.Bạn cũng cần kiến thức cơ bản về JavaScript và HTML, bạn có thể tìm thấy kiến thức này trong loạt bài Cách tạo trang web bằng HTML và trong Cách viết mã trong JavaScript . Kiến thức cơ bản về CSS cũng sẽ hữu ích, bạn có thể tìm thấy kiến thức này tại Mạng nhà phát triển Mozilla .
Bạn sẽ sử dụng các thành phần React,
useState
Hook vàuseReducer
Hook, mà bạn có thể tìm hiểu trong hướng dẫn của ta Cách tạo các thành phần tùy chỉnh trong React và Cách quản lý trạng thái bằng Hook trên các thành phần React .
Bước 1 - Extract dữ liệu sự kiện với SyntheticEvent
Trong bước này, bạn sẽ tạo thành phần xác thực bằng cách sử dụng phần tử HTML <input>
và trình xử lý sự kiện onChange
. Thành phần này sẽ chấp nhận đầu vào và xác thực nó hoặc đảm bảo nội dung tuân theo một mẫu văn bản cụ thể.Bạn sẽ sử dụng shell bọc SyntheticEvent
để chuyển dữ liệu sự kiện vào hàm gọi lại và cập nhật thành phần bằng cách sử dụng dữ liệu từ <input>
. Bạn cũng sẽ gọi các hàm từ SyntheticEvent
, chẳng hạn như preventDefault
để ngăn các hành động chuẩn của trình duyệt.
Trong React, bạn không cần phải chọn các phần tử trước khi thêm trình nghe sự kiện. Thay vào đó, bạn thêm trình xử lý sự kiện trực tiếp vào JSX của bạn bằng cách sử dụng đạo cụ. Có một số lượng lớn các sự kiện được hỗ trợ trong React , bao gồm các sự kiện phổ biến như onClick
hoặc onChange
và các sự kiện ít phổ biến hơn như onWheel
.
Không giống như các trình xử lý onevent
DOM root , React chuyển một shell bọc đặc biệt có tên là SyntheticEvent
tới trình xử lý sự kiện chứ không phải là Event
trình duyệt root . Tính trừu tượng giúp giảm sự không nhất quán giữa các trình duyệt và cung cấp cho các thành phần của bạn một giao diện chuẩn để làm việc với các sự kiện. API cho SyntheticEvent
tương tự như Event
root , vì vậy hầu hết các việc đều được thực hiện theo cùng một cách.
Để chứng minh điều này, bạn sẽ bắt đầu bằng cách thực hiện đầu vào xác thực của bạn . Đầu tiên, bạn sẽ tạo một thành phần có tên là FileNamer
. Đây sẽ là một phần tử <form>
với đầu vào để đặt tên file . Khi điền thông tin đầu vào, bạn sẽ thấy thông tin cập nhật hộp xem trước phía trên thành phần. Thành phần cũng sẽ bao gồm một nút gửi để chạy xác thực, nhưng đối với ví dụ này, biểu mẫu sẽ không thực sự gửi bất cứ thứ gì.
Đầu tiên, tạo folder :
- mkdir src/components/FileNamer
Sau đó, mở FileNamer.js
trong editor của bạn:
- nano src/components/FileNamer/FileNamer.js
Bên trong FileNamer.js
, hãy tạo một shell bọc <div>
, sau đó thêm một <div>
có tên lớp là preview
và một phần tử <form>
bên trong shell bọc bằng cách viết các dòng mã sau:
import React from 'react'; export default function FileNamer() { return( <div className="wrapper"> <div className="preview"> </div> <form> </form> </div> ) }
Tiếp theo, thêm một yếu tố đầu vào cho tên để hiển thị trong hộp xem trước và nút Lưu . Thêm các dòng được đánh dấu sau:
import React from 'react'; export default function FileNamer() { return( <div className="wrapper"> <div className="preview"> <h2>Preview:</h2> </div> <form> <label> <p>Name:</p> <input name="name" /> </label> <div> <button>Save</button> </div> </form> </div> ) }
Trong preview
<div>
, bạn đã thêm phần tử <h2>
với bản Preview
văn bản. Đây sẽ là hộp xem trước của bạn. Bên trong biểu mẫu của bạn , bạn đã thêm một <input>
bao quanh bởi một phần tử <label>
với Name:
làm văn bản của nó. Sau đó, bạn đã thêm một button
tên Lưu trực tiếp trước thẻ đóng <form>
.
Lưu và đóng file .
Tiếp theo, mở App.js
:
- nano src/components/App/App.js
Nhập FileNamer
, sau đó hiển thị bên trong chức năng App
bằng cách thêm các dòng được đánh dấu sau:
import React from 'react'; import FileNamer from '../FileNamer/FileNamer'; function App() { return <FileNamer /> } export default App;
Lưu và đóng file . Khi bạn thực hiện, trình duyệt sẽ làm mới và bạn sẽ thấy thành phần của bạn .
Tiếp theo, thêm một số kiểu sáng để giúp xác định các phần và thêm một số phần đệm và lề cho các phần tử.
Mở FileNamer.css
trong editor của bạn:
- nano src/components/FileNamer/FileNamer.css
Cung cấp cho lớp .preview
một đường viền và phần đệm màu xám, sau đó cung cấp cho lớp .wrapper
một lượng nhỏ phần đệm. Hiển thị các mục trong một cột bằng cách sử dụng flex
và flex-direction
, đồng thời căn chỉnh tất cả văn bản sang trái. Cuối cùng, xóa các kiểu nút mặc định bằng cách xóa đường viền và thêm đường viền màu đen:
.preview { border: 1px darkgray solid; padding: 10px; } .wrapper { display: flex; flex-direction: column; padding: 20px; text-align: left; } .wrapper button { background: none; border: 1px black solid; margin-top: 10px; }
Lưu và đóng file . Sau đó, mở FileNamer.js
:
- nano src/components/FileNamer/FileNamer.js
Nhập các kiểu để áp dụng chúng cho thành phần của bạn:
import React from 'react'; import './FileNamer.css'; export default function FileNamer() { return( <div className="wrapper"> <div className="preview"> <h2>Preview:</h2> </div> <form> <label> <p>Name:</p> <input name="name" /> </label> <div> <button>Save</button> </div> </form> </div> ) }
Lưu các file . Khi bạn làm như vậy, trình duyệt sẽ làm mới và bạn sẽ thấy thành phần có các kiểu mới.
Đến đây bạn đã có một thành phần cơ bản, bạn có thể thêm trình xử lý sự kiện <input>
phần tử <input>
. Nhưng trước tiên, bạn cần một nơi để lưu trữ dữ liệu trong trường đầu vào. Thêm useState
Hook để giữ đầu vào:
import React, { useState } from 'react'; import './FileNamer.css'; export default function FileNamer() { const [name, setName] = useState(''); return( <div className="wrapper"> <div className="preview"> <h2>Preview: {name}.js</h2> </div> <form> <label> <p>Name:</p> <input name="name" /> </label> <div> <button>Save</button> </div> </form> </div> ) }
Trong đoạn mã này, bạn đã hủy useState
thành một name
biến để giữ dữ liệu đầu vào và một hàm có tên setName
để cập nhật dữ liệu. Sau đó, bạn hiển thị name
trong phần xem trước, theo sau là phần mở rộng .js
, như thể user đang đặt tên file .
Đến đây bạn có thể lưu trữ dữ liệu đầu vào, bạn có thể thêm trình xử lý sự kiện <input>
thành phần <input>
. Thường có một số trình xử lý sự kiện khác nhau mà bạn có thể sử dụng cho một nhiệm vụ nhất định. Trong trường hợp này, ứng dụng của bạn cần nắm bắt dữ liệu mà user nhập vào phần tử. Trình xử lý phổ biến nhất cho tình huống này là onChange
, sẽ kích hoạt mỗi khi thành phần thay đổi. Tuy nhiên, bạn cũng có thể sử dụng các sự kiện bàn phím , chẳng hạn như onKeyDown
, onKeyPress
và onKeyUp
. Sự khác biệt chủ yếu liên quan đến thời điểm sự kiện kích hoạt và thông tin được chuyển đến đối tượng SyntheticEvent
. Ví dụ: onBlur
, một sự kiện khi một phần tử trở nên không tập trung, sẽ kích hoạt trước onClick
. Nếu bạn muốn xử lý thông tin user trước khi một sự kiện khác xảy ra, bạn có thể chọn một sự kiện trước đó.
Sự lựa chọn sự kiện của bạn cũng được xác định bởi loại dữ liệu bạn muốn chuyển đến SyntheticEvent
. Ví dụ: sự kiện onKeyPress
sẽ bao gồm charCode
của phím mà user đã nhấn, trong khi onChange
sẽ không bao gồm mã ký tự cụ thể, nhưng sẽ bao gồm đầu vào đầy đủ. Điều này rất quan trọng nếu bạn muốn thực hiện các hành động khác nhau tùy thuộc vào phím mà user đã nhấn.
Đối với hướng dẫn này, hãy sử dụng onChange
để nắm bắt toàn bộ giá trị đầu vào chứ không chỉ khóa mới nhất . Điều này sẽ giúp bạn tiết kiệm công sức lưu trữ và nối giá trị trên mỗi thay đổi.
Tạo một hàm nhận event
làm đối số và chuyển nó <input>
phần tử <input>
bằng cách sử dụng onChange
prop:
import React, { useState } from 'react'; import './FileNamer.css'; export default function FileNamer() { const [name, setName] = useState(''); return( <div className="wrapper"> <div className="preview"> <h2>Preview: {name}.js</h2> </div> <form> <label> <p>Name:</p> <input name="name" onChange={event => {}}/> </label> <div> <button>Save</button> </div> </form> </div> ) }
Như đã đề cập trước đó, event
ở đây không phải là sự kiện trình duyệt root . Đó là SyntheticEvent
được cung cấp bởi React, thường được xử lý giống nhau. Trong trường hợp hiếm hoi bạn cần sự kiện root , bạn có thể sử dụng thuộc tính nativeEvent
trên SyntheticEvent
.
Đến đây bạn đã có sự kiện, hãy lấy ra giá trị hiện tại từ thuộc tính target.value
của sự kiện. Chuyển giá trị cho setName
để cập nhật bản xem trước:
import React, { useState } from 'react'; import './FileNamer.css'; export default function FileNamer() { const [name, setName] = useState(''); return( <div className="wrapper"> <div className="preview"> <h2>Preview: {name}.js</h2> </div> <form> <label> <p>Name:</p> <input autoComplete="off" name="name" onChange={event => setName(event.target.value) } /> </label> <div> <button>Save</button> </div> </form> </div> ) }
Ngoài ra, bạn đặt thuộc tính autoComplete
thành "off"
để tắt các đề xuất của trình duyệt.
Lưu các file . Khi bạn làm như vậy, trang sẽ reload và khi bạn nhập <input>
bạn sẽ thấy bản cập nhật trong bản xem trước.
Lưu ý: Bạn cũng có thể truy cập tên của đầu vào bằng cách sử dụng event.target.name
. Điều này sẽ hữu ích nếu bạn đang sử dụng cùng một trình xử lý sự kiện trên nhiều đầu vào, vì name
sẽ tự động trùng với thuộc tính name
của thành phần.
Đến đây, bạn có một trình xử lý sự kiện đang hoạt động. Bạn đang lấy thông tin user , lưu nó về trạng thái và cập nhật thành phần khác với dữ liệu. Nhưng ngoài việc lấy thông tin từ một sự kiện, có những tình huống bạn cần phải tạm dừng một sự kiện, chẳng hạn như nếu bạn muốn ngăn việc gửi biểu mẫu hoặc ngăn hành động nhấn phím.
Để dừng một sự kiện, hãy gọi hành động preventDefault
đối với sự kiện. Điều này sẽ ngăn trình duyệt thực hiện hành vi mặc định.
Trong trường hợp của thành phần FileNamer
, có một số ký tự nhất định có thể phá vỡ quy trình chọn file mà ứng dụng của bạn nên cấm. Ví dụ: bạn sẽ không muốn user thêm dấu *
vào tên file vì nó xung đột với ký tự đại diện, ký tự này có thể được hiểu là để chỉ một group file khác. Trước khi user có thể gửi biểu mẫu, bạn cần kiểm tra đảm bảo rằng không có ký tự không hợp lệ. Nếu có ký tự không hợp lệ, bạn sẽ ngăn trình duyệt gửi biểu mẫu và hiển thị thông báo cho user .
Đầu tiên, tạo một Hook sẽ tạo một boolean alert
và một hàm setAlert
. Sau đó, thêm một <div>
để hiển thị thông alert
nếu alert
là đúng:
import React, { useState } from 'react'; import './FileNamer.css'; export default function FileNamer() { const [name, setName] = useState(''); const [alert, setAlert] = useState(false); return( <div className="wrapper"> <div className="preview"> <h2>Preview: {name}.js</h2> </div> <form> <label> <p>Name:</p> <input autoComplete="off" name="name" onChange={event => setName(event.target.value) } /> </label> {alert && <div> Forbidden Character: *</div>} <div> <button>Save</button> </div> </form> </div> ) }
Trong mã này, bạn đã sử dụng toán tử &&
để chỉ hiển thị <div>
nếu alert
được đặt bằng true
trước. Thông báo trong <div>
sẽ cho user biết rằng ký tự *
không được phép trong đầu vào.
Tiếp theo, tạo một hàm có tên là validate
. Sử dụng phương thức .test
biểu thức chính quy để tìm xem chuỗi có chứa dấu *
. Nếu có, bạn sẽ ngăn việc gửi biểu mẫu:
import React, { useState } from 'react'; import './FileNamer.css'; export default function FileNamer() { const [name, setName] = useState(''); const [alert, setAlert] = useState(false); const validate = event => { if(/\*/.test(name)) { event.preventDefault(); setAlert(true); return; } setAlert(false); }; return( <div className="wrapper"> <div className="preview"> <h2>Preview: {name}.js</h2> </div> <form> <label> <p>Name:</p> <input autoComplete="off" name="name" onChange={event => setName(event.target.value) } /> </label> {alert && <div> Forbidden Character: *</div>} <div> <button onClick={validate}>Save</button> </div> </form> </div> ) }
Khi hàm validate
được gọi và kiểm tra trả về true
, nó sẽ sử dụng event.preventDefault
sau đó gọi setAlert(true)
. Nếu không, nó sẽ gọi setAlert(false)
. Trong phần cuối cùng của mã, bạn đã thêm trình xử lý sự kiện vào phần tử <button>
với onClick
.
Lưu các file . Như trước đây, bạn cũng có thể sử dụng onMouseDown
, nhưng onClick
phổ biến hơn và do đó cho phép bạn tránh mọi tác dụng phụ không mong muốn. Biểu mẫu này không có bất kỳ hành động gửi nào, nhưng bằng cách ngăn hành động mặc định, bạn ngăn trang reload :
Đến đây bạn có một biểu mẫu sử dụng hai trình xử lý sự kiện: onChange
và onClick
. Bạn đang sử dụng trình xử lý sự kiện để kết nối hành động của user với thành phần và ứng dụng, làm cho nó trở nên tương tác. Khi làm như vậy, bạn đã học cách thêm sự kiện vào các phần tử DOM và cách có một số sự kiện kích hoạt trên cùng một hành động, nhưng cung cấp thông tin khác nhau trong SyntheticEvent
. Bạn cũng đã học cách extract thông tin từ SyntheticEvent
, cập nhật các thành phần khác bằng cách lưu dữ liệu đó về trạng thái và tạm dừng một sự kiện bằng cách sử dụng preventDefault
.
Trong bước tiếp theo, bạn sẽ thêm nhiều sự kiện vào một phần tử DOM để xử lý nhiều hành động khác nhau của user .
Bước 2 - Thêm nhiều trình xử lý sự kiện vào cùng một phần tử
Có những trường hợp khi một thành phần duy nhất sẽ kích hoạt nhiều sự kiện và bạn cần có khả năng kết nối với các sự kiện khác nhau trên một thành phần duy nhất. Ví dụ: trong bước này, bạn sẽ sử dụng bộ xử lý sự kiện onFocus
và onBlur
để cung cấp cho user thông tin kịp thời về thành phần. Đến cuối bước này, bạn sẽ biết thêm về các sự kiện được hỗ trợ khác nhau trong React và cách thêm chúng vào các thành phần của bạn.
Chức năng validate
hữu ích để ngăn biểu mẫu của bạn gửi dữ liệu xấu, nhưng không hữu ích lắm cho trải nghiệm user : User chỉ nhận được thông tin về các ký tự hợp lệ sau khi họ đã điền vào toàn bộ biểu mẫu. Nếu có nhiều trường, nó sẽ không cung cấp cho user bất kỳ phản hồi nào cho đến bước cuối cùng. Để làm cho thành phần này thân thiện hơn với user , hãy hiển thị các ký tự được phép và không được phép khi user vào trường bằng cách thêm trình xử lý sự kiện onFocus
.
Đầu tiên, hãy cập nhật alert
<div>
để bao gồm thông tin về những ký tự được phép. Cho user biết các ký tự chữ và số được phép và dấu *
không được phép:
import React, { useState } from 'react'; import './FileNamer.css'; export default function FileNamer() { ... return( <div className="wrapper"> <div className="preview"> <h2>Preview: {name}.js</h2> </div> <form> <label> <p>Name:</p> <input autocomplete="off" name="name" onChange={event => setName(event.target.value) } /> </label> {alert && <div> <span role="img" aria-label="allowed">✅</span> Alphanumeric Characters <br /> <span role="img" aria-label="not allowed">⛔️</span> * </div> } <div> <button onClick={validate}>Save</button> </div> </form> </div> ) }
Trong mã này, bạn đã sử dụng tiêu chuẩn Ứng dụng Internet đa dạng có thể truy cập (ARIA) để làm cho thành phần dễ tiếp cận hơn với trình đọc màn hình.
Tiếp theo, thêm một trình xử lý sự kiện khác <input>
phần tử <input>
. Bạn sẽ cảnh báo user về các ký tự được phép và không được phép khi họ kích hoạt thành phần bằng cách nhấp hoặc tab vào đầu vào. Thêm vào dòng được đánh dấu sau:
import React, { useState } from 'react'; import './FileNamer.css'; export default function FileNamer() { ... return( <div className="wrapper"> <div className="preview"> <h2>Preview: {name}.js</h2> </div> <form> <label> <p>Name:</p> <input autocomplete="off" name="name" onChange={event => setName(event.target.value) } onFocus={() => setAlert(true)} /> </label> {alert && <div> <span role="img" aria-label="allowed">✅</span> Alphanumeric Characters <br /> <span role="img" aria-label="not allowed">⛔️</span> * </div> } <div> <button onClick={validate}>Save</button> </div> </form> </div> ) }
Bạn đã thêm trình xử lý sự kiện onFocus
<input>
phần tử <input>
. Sự kiện này kích hoạt khi user chọn trường. Sau khi thêm trình xử lý sự kiện, bạn đã chuyển một hàm ẩn danh cho onFocus
sẽ gọi setAlert(true)
và hiển thị dữ liệu. Trong trường hợp này, bạn không cần bất kỳ thông tin nào từ SyntheticEvent
; bạn chỉ cần kích hoạt một sự kiện khi user hành động. React vẫn đang gửi SyntheticEvent
đến hàm, nhưng trong tình huống hiện tại, bạn không cần sử dụng thông tin trong đó.
Lưu ý: Bạn có thể kích hoạt hiển thị dữ liệu với onClick
hoặc thậm chí onMouseDown
, nhưng điều đó sẽ không thể truy cập được đối với user sử dụng bàn phím để chuyển tab vào các trường biểu mẫu. Trong trường hợp này, sự kiện onFocus
sẽ xử lý cả hai trường hợp.
Lưu các file . Khi bạn làm như vậy, trình duyệt sẽ làm mới và thông tin sẽ vẫn ẩn cho đến khi user nhấp vào đầu vào.
Thông tin user hiện xuất hiện khi trường được lấy tiêu điểm, nhưng bây giờ dữ liệu hiện diện trong suốt thời gian của thành phần. Không có cách nào để làm cho nó biến mất. May mắn là có một sự kiện khác được gọi là onBlur
kích hoạt khi user để lại thông tin đầu vào. Thêm trình xử lý sự kiện onBlur
với chức năng ẩn danh sẽ đặt alert
thành false
. Giống như onFocus
, điều này sẽ hoạt động cả khi user nhấp vào hoặc khi user rời khỏi tab:
import React, { useState } from 'react'; import './FileNamer.css'; export default function FileNamer() { ... return( <div className="wrapper"> <div className="preview"> <h2>Preview: {name}.js</h2> </div> <form> <label> <p>Name:</p> <input autocomplete="off" name="name" onBlur={() => setAlert(false)} onChange={event => setName(event.target.value) } onFocus={() => setAlert(true)} /> </label> {alert && <div> <span role="img" aria-label="allowed">✅</span> Alphanumeric Characters <br /> <span role="img" aria-label="not allowed">⛔️</span> * </div> } <div> <button onClick={validate}>Save</button> </div> </form> </div> ) }
Lưu các file . Khi bạn làm như vậy, trình duyệt sẽ làm mới và thông tin sẽ hiển thị khi user nhấp vào phần tử và biến mất khi user nhấp vào:
Bạn có thể thêm bao nhiêu trình xử lý sự kiện vào một phần tử. Nếu bạn có ý tưởng về một sự kiện bạn cần, nhưng không chắc về tên, hãy cuộn qua các sự kiện được hỗ trợ và bạn có thể tìm thấy thứ mình cần.
Trong bước này, bạn đã thêm nhiều trình xử lý sự kiện vào một phần tử DOM. Bạn đã biết cách các trình xử lý sự kiện khác nhau có thể xử lý nhiều sự kiện — chẳng hạn như cả nhấp chuột và tab — hoặc một phạm vi sự kiện hẹp.
Trong bước tiếp theo, bạn sẽ thêm trình nghe sự kiện toàn cục vào đối tượng Window
để nắm bắt các sự kiện xảy ra bên ngoài thành phần trực tiếp.
Bước 3 - Thêm sự kiện cửa sổ
Trong bước này, bạn sẽ đưa thông tin user vào một thành phần bật lên sẽ kích hoạt khi user tập trung một đầu vào và sẽ đóng khi user nhấp vào bất kỳ nơi nào khác trên trang. Để đạt được hiệu ứng này, bạn sẽ thêm trình nghe sự kiện toàn cục vào đối tượng Window
bằng useEffect
Hook . Bạn cũng sẽ xóa trình xử lý sự kiện khi thành phần ngắt kết nối để ngăn rò rỉ bộ nhớ, khi ứng dụng của bạn chiếm nhiều bộ nhớ hơn mức cần thiết.
Khi kết thúc bước này, bạn có thể thêm và xóa trình nghe sự kiện một cách an toàn trên các thành phần riêng lẻ. Bạn cũng sẽ học cách sử dụng useEffect
Hook để thực hiện các hành động khi một thành phần gắn và ngắt kết nối.
Trong hầu hết các trường hợp, bạn sẽ thêm trình xử lý sự kiện trực tiếp vào các phần tử DOM trong JSX của bạn . Điều này giữ cho mã của bạn tập trung và ngăn ngừa các tình huống khó hiểu trong đó một thành phần đang kiểm soát hành vi của thành phần khác thông qua đối tượng Window
. Nhưng có những lúc bạn cần thêm trình nghe sự kiện global . Ví dụ: bạn có thể cần một trình nghe cuộn tải nội dung mới hoặc bạn có thể cần nắm bắt các sự kiện nhấp chuột bên ngoài một thành phần.
Trong hướng dẫn này, bạn chỉ muốn hiển thị cho user thông tin về đầu vào nếu họ yêu cầu cụ thể. Sau khi hiển thị thông tin, bạn cần ẩn thông tin đó khi nào user nhấp vào trang bên ngoài thành phần.
Để bắt đầu, di chuyển các alert
hiển thị thành một mới <div>
với một className
của information-wrapper
. Sau đó, thêm một nút mới với className
information
và sự kiện onClick
sẽ gọi setAlert(true)
:
import React, { useState } from 'react'; import './FileNamer.css'; export default function FileNamer() { ... return( <div className="wrapper"> <div className="preview"> <h2>Preview: {name}.js</h2> </div> <form> <label> <p>Name:</p> <input autocomplete="off" name="name" onChange={event => setName(event.target.value) } /> </label> <div className="information-wrapper"> <button className="information" onClick={() => setAlert(true)} type="button" > more information </button> {alert && <div className="popup"> <span role="img" aria-label="allowed">✅</span> Alphanumeric Characters <br /> <span role="img" aria-label="not allowed">⛔️</span> * </div> } </div> <div> <button onClick={validate}>Save</button> </div> </form> </div> ) }
Bạn cũng đã xóa các trình xử lý onFocus
và onBlur
khỏi phần tử <input>
để loại bỏ hành vi từ bước cuối cùng.
Lưu và đóng file . Sau đó, mở FileNamer.css
:
- nano src/components/FileNamer/FileNamer.css
Thêm một số kiểu để định vị hoàn toàn thông tin popup
phía trên nút. Sau đó, thay đổi <button>
với một lớp information
thành màu xanh lam không có đường viền.
.information { font-size: .75em; color: blue; cursor: pointer; } .wrapper button.information { border: none; } .information-wrapper { position: relative; } .popup { position: absolute; background: white; border: 1px darkgray solid; padding: 10px; top: -70px; left: 0; } .preview { border: 1px darkgray solid; padding: 10px; } .wrapper { display: flex; flex-direction: column; padding: 20px; text-align: left; } .wrapper button { background: none; border: 1px black solid; margin-top: 10px; }
Lưu và đóng file . Khi bạn thực hiện, trình duyệt sẽ reload và khi bạn nhấp vào more information
, thông tin về thành phần sẽ xuất hiện:
Đến đây bạn có thể kích hoạt cửa sổ bật lên, nhưng không có cách nào để xóa nó. Để khắc phục sự cố đó, hãy thêm trình xử lý sự kiện chung gọi setAlert(false)
vào bất kỳ lần nhấp nào bên ngoài cửa sổ bật lên.
Trình nghe sự kiện sẽ trông giống như sau:
window.addEventListener('click', () => setAlert(false))
Tuy nhiên, bạn phải lưu ý về thời điểm đặt trình xử lý sự kiện trong mã của bạn . Ví dụ: bạn không thể thêm trình xử lý sự kiện ở đầu mã thành phần của bạn , vì sau đó mỗi khi có gì đó thay đổi, thành phần sẽ hiển thị lại và thêm trình xử lý sự kiện mới. Vì thành phần của bạn có thể sẽ hiển thị lại nhiều lần, điều đó sẽ tạo ra rất nhiều trình nghe sự kiện không sử dụng chiếm bộ nhớ.
Để giải quyết vấn đề này, React có một Hook đặc biệt gọi là useEffect
sẽ chỉ chạy khi các thuộc tính cụ thể thay đổi. Cấu trúc cơ bản là:
useEffect(() => { // run code when anything in the array changes }, [someProp, someOtherProp])
Trong ví dụ đơn giản, React sẽ chạy mã trong hàm ẩn danh khi nào someProp
hoặc someOtherProp
thay đổi. Các mục trong mảng được gọi là phụ thuộc . Hook này lắng nghe các thay đổi đối với các phụ thuộc và sau đó chạy hàm sau khi thay đổi.
Như vậy, bạn có các công cụ để thêm và xóa trình xử lý sự kiện toàn cục một cách an toàn bằng cách sử dụng useEffect
để thêm trình xử lý sự kiện khi nào alert
là true
và xóa nó khi nào alert
là false
.
Còn một bước nữa. Khi thành phần ngắt kết nối, nó sẽ chạy bất kỳ chức năng nào mà bạn trả về từ bên trong useEffect
Hook. Do đó, bạn cũng cần trả về một hàm loại bỏ trình xử lý sự kiện khi thành phần ngắt kết nối.
Cấu trúc cơ bản sẽ như thế này:
useEffect(() => { // run code when anything in the array changes return () => {} // run code when the component unmounts }, [someProp, someOtherProp])
Đến đây bạn đã biết hình dạng của useEffect
Hook, hãy sử dụng nó trong ứng dụng của bạn. Mở FileNamer.js
:
- nano src/components/FileNamer/FileNamer.js
Bên trong, nhập useEffect
, sau đó thêm một hàm ẩn danh trống với phụ thuộc là alert
và setAlert
trong mảng sau hàm:
import React, { useEffect, useState } from 'react'; import './FileNamer.css'; export default function FileNamer() { const [name, setName] = useState(''); const [alert, setAlert] = useState(false); useEffect(() => { }, [alert, setAlert]); ...
Trong mã này, bạn đã thêm cả alert
và setAlert
. Để hoàn thiện, React khuyên bạn nên thêm tất cả các phụ thuộc bên ngoài vào hàm useEffect
. Vì bạn sẽ gọi hàm setAlert
, nó có thể được coi là một phụ thuộc. setAlert
sẽ không thay đổi sau lần hiển thị đầu tiên, nhưng bạn nên đưa bất kỳ thứ gì có thể được coi là phụ thuộc vào.
Tiếp theo, bên trong hàm ẩn danh, hãy tạo một hàm mới có tên là handleWindowClick
gọi setAlert(false)
:
import React, { useEffect, useState } from 'react'; import './FileNamer.css'; export default function FileNamer() { const [name, setName] = useState(''); const [alert, setAlert] = useState(false); useEffect(() => { const handleWindowClick = () => setAlert(false) }, [alert, setAlert]); ... }
Sau đó, thêm một điều kiện sẽ gọi window.addEventListener('click', handleWindowClick)
khi alert
là true
và sẽ gọi window.removeEventListener('click', handleWindowClick)
khi alert
là false
. Điều này sẽ thêm trình xử lý sự kiện mỗi khi bạn kích hoạt cửa sổ bật lên và xóa nó mỗi khi cửa sổ bật lên đóng lại:
import React, { useEffect, useState } from 'react'; import './FileNamer.css'; export default function FileNamer() { const [name, setName] = useState(''); const [alert, setAlert] = useState(false); useEffect(() => { const handleWindowClick = () => setAlert(false) if(alert) { window.addEventListener('click', handleWindowClick); } else { window.removeEventListener('click', handleWindowClick); } }, [alert, setAlert]); ... }
Cuối cùng, trả về một hàm sẽ loại bỏ trình lắng nghe sự kiện. , điều này sẽ chạy khi thành phần ngắt kết nối. Có thể không có trình nghe sự kiện trực tiếp, nhưng nó vẫn đáng để dọn dẹp trong các tình huống mà trình nghe vẫn tồn tại:
import React, { useEffect, useState } from 'react'; import './FileNamer.css'; export default function FileNamer() { const [name, setName] = useState(''); const [alert, setAlert] = useState(false); useEffect(() => { const handleWindowClick = () => setAlert(false) if(alert) { window.addEventListener('click', handleWindowClick); } else { window.removeEventListener('click', handleWindowClick) } return () => window.removeEventListener('click', handleWindowClick); }, [alert, setAlert]); ... }
Lưu các file . Khi bạn làm như vậy, trình duyệt sẽ làm mới. Nếu bạn bấm vào nút thông tin thêm , thông báo sẽ xuất hiện. Nếu bạn nhìn vào trình nghe sự kiện global trong các công cụ dành cho nhà phát triển, bạn sẽ thấy có trình xử lý click
:
Nhấp vào bất kỳ đâu bên ngoài thành phần. Thông báo sẽ không xuất hiện và bạn sẽ không còn thấy trình nghe sự kiện nhấp chuột global .
Việc sử useEffect
Hook của bạn đã thêm và xóa thành công trình xử lý sự kiện global dựa trên tương tác của user . Nó không bị ràng buộc với một phần tử DOM cụ thể, nhưng thay vào đó được kích hoạt bởi sự thay đổi trong trạng thái thành phần.
Lưu ý: Từ quan điểm trợ năng, thành phần này chưa hoàn chỉnh. Nếu user không thể sử dụng chuột, họ sẽ bị mắc kẹt với một cửa sổ bật lên đang mở vì họ sẽ không bao giờ có thể nhấp vào bên ngoài thành phần. Giải pháp sẽ là thêm một trình xử lý sự kiện khác cho keydown
cũng sẽ xóa thông báo. Mã sẽ gần giống nhau ngoại trừ phương pháp sẽ là keydown
thay vì click
.
Trong bước này, bạn đã thêm trình nghe sự kiện toàn cục bên trong một thành phần. Bạn cũng đã học cách sử dụng useEffect
Hook để thêm và xóa trình xử lý sự kiện đúng cách khi trạng thái thay đổi và cách xóa trình xử lý sự kiện khi thành phần ngắt kết nối.
Kết luận
Trình xử lý sự kiện cung cấp cho bạn cơ hội để điều chỉnh các thành phần của bạn với hành động của user . Những điều này sẽ mang đến cho các ứng dụng của bạn trải nghiệm phong phú và sẽ tăng khả năng tương tác của ứng dụng. Chúng cũng sẽ cung cấp cho bạn khả năng nắm bắt và phản hồi các hành động của user .
Trình xử lý sự kiện của React cho phép bạn tích hợp các lệnh gọi lại sự kiện của bạn với HTML để bạn có thể chia sẻ chức năng và thiết kế trên một ứng dụng. Trong hầu hết các trường hợp, bạn nên tập trung vào việc thêm trình xử lý sự kiện trực tiếp vào các phần tử DOM, nhưng trong các tình huống cần nắm bắt các sự kiện bên ngoài thành phần, bạn có thể thêm trình xử lý sự kiện và dọn dẹp chúng khi chúng không còn được sử dụng để tránh rò rỉ bộ nhớ và tạo ra các ứng dụng hiệu quả.
Nếu bạn muốn xem thêm các hướng dẫn về React, hãy xem trang Chủ đề React của ta hoặc quay lại trang Cách viết mã trong chuỗi React.js . Để tìm hiểu thêm về các sự kiện trong JavaScript, hãy đọc các hướng dẫn Tìm hiểu Sự kiện trong JavaScript và Sử dụng Trình phát Sự kiện trong Node.js của ta .
Các tin liên quan