React 事件处理完整指南

1. 基础事件处理

1.1 事件绑定方式

// 类组件中的事件处理
class ButtonComponent extends React.Component {
  // 方法一:类字段语法(推荐)
  handleClick = (e) => {
    console.log('Button clicked', e);
  }

  // 方法二:构造函数中绑定
  constructor(props) {
    super(props);
    this.handleClick2 = this.handleClick2.bind(this);
  }

  handleClick2() {
    console.log('Button clicked 2');
  }

  render() {
    return (
      <div>
        {/* 方法一:使用类字段定义的方法 */}
        <button onClick={this.handleClick}>Click Me</button>

        {/* 方法二:构造函数中绑定的方法 */}
        <button onClick={this.handleClick2}>Click Me 2</button>

        {/* 方法三:内联箭头函数(不推荐,每次渲染都会创建新函数) */}
        <button onClick={(e) => this.handleClick3(e)}>Click Me 3</button>
      </div>
    );
  }
}

// 函数组件中的事件处理
function ButtonFunction() {
  const handleClick = (e) => {
    console.log('Button clicked', e);
  };

  return <button onClick={handleClick}>Click Me</button>;
}

1.2 事件对象的使用

function EventObject() {
  const handleClick = (e) => {
    // 阻止默认行为
    e.preventDefault();
    
    // 阻止事件冒泡
    e.stopPropagation();
    
    // 获取事件目标
    console.log(e.target);
    
    // 获取原生事件对象
    console.log(e.nativeEvent);
    
    // 获取事件类型
    console.log(e.type);
  };

  return (
    <a href="https://example.com" onClick={handleClick}>
      Click Me
    </a>
  );
}

2. 常见事件类型

2.1 鼠标事件

function MouseEvents() {
  const handleMouseEvents = {
    onClick: (e) => console.log('Clicked'),
    onDoubleClick: (e) => console.log('Double clicked'),
    onMouseEnter: (e) => console.log('Mouse entered'),
    onMouseLeave: (e) => console.log('Mouse left'),
    onMouseMove: (e) => console.log('Mouse moved', e.clientX, e.clientY),
    onMouseDown: (e) => console.log('Mouse down'),
    onMouseUp: (e) => console.log('Mouse up')
  };

  return (
    <div 
      style={{ width: 200, height: 200, background: '#f0f0f0' }}
      {...handleMouseEvents}
    >
      Mouse Events Demo
    </div>
  );
}

2.2 键盘事件

function KeyboardEvents() {
  const handleKeyDown = (e) => {
    console.log('Key pressed:', e.key);
    console.log('Key code:', e.keyCode);
    console.log('Ctrl key:', e.ctrlKey);
    console.log('Alt key:', e.altKey);
    console.log('Shift key:', e.shiftKey);
  };

  const handleKeyUp = (e) => {
    console.log('Key released:', e.key);
  };

  return (
    <input
      type="text"
      onKeyDown={handleKeyDown}
      onKeyUp={handleKeyUp}
      placeholder="Type something..."
    />
  );
}

2.3 表单事件

function FormEvents() {
  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('Form submitted');
  };

  const handleChange = (e) => {
    console.log('Input value:', e.target.value);
  };

  const handleFocus = () => {
    console.log('Input focused');
  };

  const handleBlur = () => {
    console.log('Input lost focus');
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        onChange={handleChange}
        onFocus={handleFocus}
        onBlur={handleBlur}
      />
      <button type="submit">Submit</button>
    </form>
  );
}

3. 事件处理进阶

3.1 事件委托

function EventDelegation() {
  const handleClick = (e) => {
    // 检查事件目标
    if (e.target.tagName === 'LI') {
      console.log('Clicked item:', e.target.textContent);
    }
  };

  return (
    <ul onClick={handleClick}>
      <li>Item 1</li>
      <li>Item 2</li>
      <li>Item 3</li>
    </ul>
  );
}

3.2 自定义事件处理

function CustomEventComponent() {
  useEffect(() => {
    // 创建自定义事件
    const customEvent = new CustomEvent('myCustomEvent', {
      detail: { message: 'Hello from custom event' }
    });

    // 添加事件监听器
    const handleCustomEvent = (e) => {
      console.log(e.detail.message);
    };

    document.addEventListener('myCustomEvent', handleCustomEvent);

    // 触发自定义事件
    document.dispatchEvent(customEvent);

    // 清理
    return () => {
      document.removeEventListener('myCustomEvent', handleCustomEvent);
    };
  }, []);

  return <div>Custom Event Demo</div>;
}

3.3 事件节流和防抖

function ThrottleAndDebounce() {
  // 防抖
  const debounce = (fn, delay) => {
    let timeoutId;
    return (...args) => {
      clearTimeout(timeoutId);
      timeoutId = setTimeout(() => fn(...args), delay);
    };
  };

  // 节流
  const throttle = (fn, delay) => {
    let lastCall = 0;
    return (...args) => {
      const now = Date.now();
      if (now - lastCall >= delay) {
        fn(...args);
        lastCall = now;
      }
    };
  };

  // 使用防抖的事件处理器
  const handleChangeDebounced = debounce((e) => {
    console.log('Debounced value:', e.target.value);
  }, 500);

  // 使用节流的事件处理器
  const handleMoveThrottled = throttle((e) => {
    console.log('Throttled position:', e.clientX, e.clientY);
  }, 100);

  return (
    <div>
      <input
        type="text"
        onChange={handleChangeDebounced}
        placeholder="Debounced input"
      />
      <div
        onMouseMove={handleMoveThrottled}
        style={{ height: 200, background: '#f0f0f0' }}
      >
        Move mouse here
      </div>
    </div>
  );
}

4. 合成事件

4.1 事件池

function EventPoolExample() {
  const handleClick = (e) => {
    // React 17之前需要使用 e.persist()
    // e.persist();
    
    setTimeout(() => {
      console.log(e.type); // 在 React 17+ 中可以正常工作
    }, 0);
  };

  return <button onClick={handleClick}>Click Me</button>;
}

4.2 跨浏览器兼容性

function CrossBrowserEvent() {
  const handleWheel = (e) => {
    // React 统一了滚轮事件
    console.log('Delta Y:', e.deltaY);
  };

  return (
    <div onWheel={handleWheel} style={{ height: 200, background: '#f0f0f0' }}>
      Scroll here
    </div>
  );
}

5. 最佳实践

5.1 性能优化

function OptimizedEventHandling() {
  // 使用 useCallback 缓存事件处理函数
  const handleClick = useCallback((e) => {
    console.log('Clicked');
  }, []);

  // 使用 useMemo 缓存计算结果
  const memoizedValue = useMemo(() => {
    return expensiveCalculation();
  }, []);

  return (
    <button onClick={handleClick}>
      Click Me ({memoizedValue})
    </button>
  );
}

5.2 TypeScript 集成

interface EventProps {
  onCustomClick: (value: string) => void;
}

function TypedEventComponent({ onCustomClick }: EventProps) {
  const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
    onCustomClick(e.currentTarget.value);
  };

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    console.log(e.target.value);
  };

  return (
    <div>
      <button onClick={handleClick} value="button-value">
        Click Me
      </button>
      <input onChange={handleChange} />
    </div>
  );
}

6. 总结

事件处理要点:

  1. 选择合适的事件绑定方式
  2. 正确处理事件对象
  3. 注意性能优化
  4. 适当使用事件委托
  5. 处理好事件清理工作

最佳实践:

  1. 使用类字段语法或 hooks 定义事件处理器
  2. 避免在 JSX 中使用内联函数
  3. 合理使用防抖和节流
  4. 使用 TypeScript 增加类型安全
  5. 注意内存泄漏问题