react学习之事件处理

我爱海鲸 2026-05-02 15:20:44 暂无标签

简介vite、React 事件绑定、事件对象 e、阻止默认行为、阻止冒泡

App.jsx:

import { useEffect, useState } from 'react'
import './App.css'

export default function App() {
  const [count, setCount] = useState(0)
  const [eventHint, setEventHint] = useState('点击下面按钮查看合成事件信息')
  const [bubbleLog, setBubbleLog] = useState([])
  const [formStatus, setFormStatus] = useState('')
  const [linkMsg, setLinkMsg] = useState('')

  useEffect(() => {
    document.title = 'React 事件处理'
  }, [])

  const pushBubble = (line) => {
    setBubbleLog((prev) => [...prev, `${new Date().toLocaleTimeString()} ${line}`])
  }

  return (
    <main className="app">
      <h1>React 事件处理</h1>
      <p className="hint">
        React 用<strong>驼峰</strong>属性绑定事件(如 <code>onClick</code>、<code>onSubmit</code>
        )。处理函数会收到<strong>合成事件</strong>对象 <code>e</code>(含 <code>preventDefault</code>、
        <code>stopPropagation</code> 等)。
      </p>

      <section className="card">
        <h2>1. 事件绑定</h2>
        <p className="hint">
          传入<strong>函数引用</strong>或<strong>箭头函数</strong>,不要加括号直接调用(除非要立即执行)。
        </p>
        <p className="count">
          计数:<strong>{count}</strong>
        </p>
        <div className="actions">
          <button type="button" onClick={() => setCount((c) => c + 1)}>
            onClick:+1
          </button>
        </div>
      </section>

      <section className="card">
        <h2>2. 事件对象 e</h2>
        <p className="hint">
          第一个参数是合成事件:<code>e.type</code>、<code>e.currentTarget</code>(绑定监听的元素)等。
        </p>
        <p className="mono">{eventHint}</p>
        <div className="actions">
          <button
            type="button"
            onClick={(e) => {
              setEventHint(
                `e.type = ${e.type};e.currentTarget = <${e.currentTarget.tagName.toLowerCase()}>`,
              )
            }}
          >
            点我读取 e
          </button>
        </div>
      </section>

      <section className="card">
        <h2>3. 阻止默认行为 preventDefault</h2>
        <p className="hint">
          表单提交、链接跳转等会有浏览器<strong>默认行为</strong>,可用 <code>e.preventDefault()</code>{' '}
          取消。
        </p>
        <form
          className="demo-form"
          onSubmit={(e) => {
            e.preventDefault()
            setFormStatus('已执行 e.preventDefault(),不会整页刷新。')
          }}
        >
          <input className="input" name="q" placeholder="随便输入" />
          <button type="submit">提交(已拦截默认提交)</button>
        </form>
        {formStatus ? <p className="mono">{formStatus}</p> : null}

        <p className="hint sub">链接示例:</p>
        <p>
          <a
            className="demo-link"
            href="https://example.com"
            onClick={(e) => {
              e.preventDefault()
              setLinkMsg('已阻止跳转 example.com(默认会被打开)')
            }}
          >
            example.com(点我只会更新下面一行字)
          </a>
        </p>
        {linkMsg ? <p className="mono">{linkMsg}</p> : null}
      </section>

      <section className="card">
        <h2>4. 阻止冒泡 stopPropagation</h2>
        <p className="hint">
          事件从里到外会<strong>冒泡</strong>。内层按钮若调用 <code>e.stopPropagation()</code>,外层{' '}
          <code>onClick</code> 不会收到这次点击。
        </p>
        <div
          className="bubble-outer"
          onClick={() => pushBubble('外层 div 收到点击')}
          role="presentation"
        >
          <p className="bubble-caption">外层(点击空白区域只会触发外层)</p>
          <div className="actions wrap">
            <button
              type="button"
              onClick={(e) => {
                e.stopPropagation()
                pushBubble('内层 A:stopPropagation → 外层不会触发')
              }}
            >
              内层 A:阻止冒泡
            </button>
            <button
              type="button"
              onClick={() => {
                pushBubble('内层按钮 B:未阻止冒泡 → 还会触发外层')
              }}
            >
              内层 B:允许冒泡
            </button>
          </div>
        </div>
        <button
          type="button"
          className="btn-ghost"
          onClick={() => setBubbleLog([])}
        >
          清空日志
        </button>
        <ul className="log">
          {bubbleLog.map((line, i) => (
            <li key={i}>{line}</li>
          ))}
        </ul>
      </section>
    </main>
  )
}

App.css:

.app {
  max-width: 36rem;
  margin: 0 auto;
  padding: 2rem 1.25rem 2.5rem;
  font-family: system-ui, sans-serif;
  line-height: 1.55;
  color: #1a1a1a;
}

.app > h1 {
  font-size: 1.28rem;
  font-weight: 650;
  margin: 0 0 0.65rem;
}

.hint {
  margin: 0 0 0.85rem;
  font-size: 0.9rem;
  color: #555;
}

.hint.sub {
  margin-top: 1rem;
  margin-bottom: 0.4rem;
}

.hint code,
.card code {
  font-size: 0.85em;
  background: #f3f4f6;
  padding: 0.08em 0.35em;
  border-radius: 4px;
}

.card {
  border: 1px solid #e5e7eb;
  border-radius: 10px;
  padding: 1rem 1.15rem 1.15rem;
  margin-bottom: 1rem;
  background: #fff;
  box-shadow: 0 1px 2px rgb(0 0 0 / 4%);
}

.card h2 {
  font-size: 1.05rem;
  font-weight: 600;
  margin: 0 0 0.5rem;
}

.count {
  margin: 0 0 0.65rem;
  font-size: 1rem;
}

.mono {
  margin: 0.5rem 0 0;
  font-family: ui-monospace, 'Cascadia Code', monospace;
  font-size: 0.88rem;
  word-break: break-word;
}

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.65rem;
}

.actions.wrap {
  margin-top: 0.5rem;
}

.actions button {
  padding: 0.45rem 0.95rem;
  font-size: 0.92rem;
  cursor: pointer;
  border: 1px solid #ccc;
  border-radius: 6px;
  background: #fff;
}

.actions button:hover {
  background: #f9fafb;
}

.demo-form {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
}

.input {
  flex: 1;
  min-width: 10rem;
  padding: 0.4rem 0.55rem;
  border: 1px solid #ccc;
  border-radius: 6px;
  font-size: 0.95rem;
}

.demo-link {
  color: #2563eb;
  text-decoration: underline;
}

.bubble-outer {
  border: 2px solid #93c5fd;
  border-radius: 10px;
  padding: 0.75rem 1rem 1rem;
  background: #eff6ff;
  cursor: default;
}

.bubble-caption {
  margin: 0 0 0.35rem;
  font-size: 0.86rem;
  color: #1e40af;
}

.btn-ghost {
  margin-top: 0.65rem;
  padding: 0.35rem 0.75rem;
  font-size: 0.85rem;
  cursor: pointer;
  border: none;
  background: transparent;
  color: #6b7280;
  text-decoration: underline;
}

.log {
  margin: 0.65rem 0 0;
  padding-left: 1.15rem;
  font-size: 0.85rem;
  color: #374151;
}

.log li {
  margin-bottom: 0.25rem;
}

相关知识点总结:

事件命名:采用驼峰式写法(onClick/onSubmit),区别于原生小写事件

绑定方式:绑定函数引用或箭头函数,不能直接调用函数(避免自动执行)

合成事件:React 封装合成事件对象 e,兼容所有浏览器,用法和原生事件一致

事件对象:可通过 e 获取 e.type/e.currentTarget 等事件信息

阻止默认行为:使用 e.preventDefault(),禁止表单刷新、链接跳转等默认行为

阻止事件冒泡:使用 e.stopPropagation(),防止事件向外层元素传递

状态更新:事件中通过 setState 更新状态,实现状态驱动视图

你好:我的2025