Khác ba chỗ: tên viết camelCase, giá trị là tham chiếu hàm chứ không phải chuỗi, và không có return false. Bên dưới còn một khác biệt lớn hơn ba cái đó — React không gắn listener lên từng thẻ.
// HTML
<button onclick="handleClick()">Lưu</button>
// JSX
<button onClick={handleClick}>Lưu</button>Cặp ngoặc cuối là lỗi hay gặp nhất: onClick={handleClick()} gọi hàm ngay lúc render rồi gán kết quả (thường là undefined) làm handler. Cần truyền tham số thì bọc trong hàm mũi tên: onClick={() => remove(id)}.
Ngăn hành vi mặc định phải gọi rõ, return false không có tác dụng:
function Form({ onSave }) {
function handleSubmit(e) {
e.preventDefault() // bắt buộc, nếu không trang sẽ tải lại
onSave()
}
return <form onSubmit={handleSubmit}>...</form>
}Đối tượng e bạn nhận được là SyntheticEvent — lớp bọc do React tạo để chuẩn hoá hành vi giữa các trình duyệt. API gần như giống hệt sự kiện gốc, và cần bản gốc thì lấy ở e.nativeEvent.
Điểm ít người biết: React không gắn listener lên từng phần tử. Nó gắn một listener duy nhất ở node gốc của ứng dụng rồi phân phối sự kiện theo cây React. Hệ quả thực tế là sự kiện từ một Portal vẫn nổi bọt lên component cha trong cây React, dù trong DOM nó nằm ở chỗ khác hẳn.
Lưu ý: Từ React 17, listener gắn ở node gốc của ứng dụng chứ không phải document như trước. Nếu bạn trộn React với thư viện cũ đọc sự kiện ở document, thứ tự nhận sự kiện có thể khác với những gì bạn nhớ.