MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

React 事件对象的常用属性与方法

2021-06-122.2k 阅读

React 事件对象概述

在 React 开发中,事件处理是构建交互式用户界面的核心部分。当一个事件发生,比如用户点击按钮、输入文本等,React 会生成一个事件对象,这个对象包含了与该事件相关的所有信息。理解并熟练运用事件对象的属性和方法,对于开发高效、灵活且用户体验良好的前端应用至关重要。

React 的事件系统采用了合成事件(SyntheticEvent)机制。合成事件是 React 自己实现的一套跨浏览器的事件系统,它模拟了原生浏览器事件的接口,在不同浏览器中保持一致的行为。这样开发者无需担心不同浏览器对事件处理的差异,大大提升了开发效率。当事件触发时,React 会将原生事件包装成合成事件对象传递给事件处理函数。

常用属性

type 属性

type 属性用于标识事件的类型。例如,常见的点击事件类型为 click,鼠标移入事件类型为 mouseenter,键盘按下事件类型为 keydown 等。通过判断 type 属性,我们可以在同一个事件处理函数中处理不同类型的事件。

import React, { useState } from 'react';

const EventTypeExample = () => {
  const [eventType, setEventType] = useState('');

  const handleEvent = (e) => {
    setEventType(e.type);
  };

  return (
    <div>
      <button onClick={handleEvent}>点击我</button>
      <p>最近触发的事件类型: {eventType}</p>
    </div>
  );
};

export default EventTypeExample;

在上述代码中,当按钮被点击时,handleEvent 函数被触发,通过 e.type 获取事件类型并更新到状态 eventType 中,进而在页面上显示出来。

target 属性

target 属性指向触发事件的 DOM 元素。这在处理多个相似元素的事件时非常有用,我们可以通过 target 来确定具体是哪个元素触发了事件。例如,在一个列表中,每个列表项都有一个点击事件,通过 target 可以知道用户点击了哪一项。

import React from'react';

const TargetExample = () => {
  const handleClick = (e) => {
    console.log('点击的元素是:', e.target.textContent);
  };

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

export default TargetExample;

在这个例子中,当点击任意一个列表项时,handleClick 函数中的 e.target.textContent 会获取到被点击列表项的文本内容并打印到控制台。

currentTarget 属性

currentTarget 属性指向绑定事件处理程序的 DOM 元素。它与 target 的区别在于,target 是实际触发事件的元素,而 currentTarget 是事件绑定的元素。这在事件冒泡的场景下尤为重要。

import React from'react';

const CurrentTargetExample = () => {
  const handleClick = (e) => {
    console.log('事件绑定的元素是:', e.currentTarget.textContent);
    console.log('实际触发事件的元素是:', e.target.textContent);
  };

  return (
    <div onClick={handleClick}>
      <p>这是一段文本</p>
      <button>点击我</button>
    </div>
  );
};

export default CurrentTargetExample;

在上述代码中,div 元素绑定了点击事件,当点击按钮或文本时,e.currentTarget 始终指向 div,而 e.target 会根据实际点击的元素而变化。

preventDefault 方法相关属性

在处理表单提交、链接点击等事件时,我们常常需要阻止默认行为。例如,表单提交时如果不希望页面刷新,链接点击时不希望跳转到新页面等。preventDefault 方法用于阻止事件的默认行为。与之相关的属性有 defaultPrevented,它表示是否已经调用过 preventDefault 方法。

import React from'react';

const PreventDefaultExample = () => {
  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('表单提交默认行为已阻止');
    console.log('是否已阻止默认行为:', e.defaultPrevented);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" placeholder="输入内容" />
      <button type="submit">提交</button>
    </form>
  );
};

export default PreventDefaultExample;

在这个表单提交的例子中,当点击提交按钮时,handleSubmit 函数被调用,e.preventDefault() 阻止了表单提交的默认页面刷新行为,同时 e.defaultPreventedtrue 表示默认行为已被阻止。

stopPropagation 方法相关属性

事件冒泡是指当一个元素上的事件被触发时,该事件会从最内层的元素开始,依次向外传播到祖先元素。stopPropagation 方法用于阻止事件冒泡。与之相关的属性有 eventPhase,它表示事件当前所处的阶段,有 1(捕获阶段)、2(目标阶段)、3(冒泡阶段)。

import React from'react';

const StopPropagationExample = () => {
  const handleInnerClick = (e) => {
    e.stopPropagation();
    console.log('内部元素点击,事件冒泡已阻止');
    console.log('事件阶段:', e.eventPhase);
  };

  const handleOuterClick = () => {
    console.log('外部元素点击');
  };

  return (
    <div onClick={handleOuterClick}>
      外部 div
      <button onClick={handleInnerClick}>内部按钮</button>
    </div>
  );
};

export default StopPropagationExample;

在上述代码中,当点击内部按钮时,handleInnerClick 函数被调用,e.stopPropagation() 阻止了事件冒泡到外部 div,此时 e.eventPhase3 表示处于冒泡阶段。

key 和 keyCode 属性(键盘事件相关)

在处理键盘事件时,keykeyCode 属性用于获取用户按下的键的信息。key 属性返回的是一个字符串,表示按下的键,例如 'a''Enter' 等;keyCode 属性返回的是一个数字代码,不同的键对应不同的代码值。不过需要注意的是,keyCode 已逐渐被弃用,推荐使用 key 属性。

import React, { useState } from'react';

const KeyExample = () => {
  const [pressedKey, setPressedKey] = useState('');

  const handleKeyDown = (e) => {
    setPressedKey(e.key);
  };

  return (
    <div>
      <input type="text" onKeyDown={handleKeyDown} />
      <p>你按下的键是: {pressedKey}</p>
    </div>
  );
};

export default KeyExample;

在这个输入框键盘事件的例子中,当用户在输入框中按下任意键时,handleKeyDown 函数通过 e.key 获取按下的键并更新到 pressedKey 状态,从而在页面上显示出来。

clientX 和 clientY 属性(鼠标事件相关)

clientXclientY 属性用于获取鼠标指针在浏览器窗口中的坐标位置。在实现一些与鼠标位置相关的交互,如拖拽、绘制等功能时非常有用。

import React, { useState } from'react';

const MousePositionExample = () => {
  const [mouseX, setMouseX] = useState(0);
  const [mouseY, setMouseY] = useState(0);

  const handleMouseMove = (e) => {
    setMouseX(e.clientX);
    setMouseY(e.clientY);
  };

  return (
    <div style={{ height: '100vh', width: '100vw' }} onMouseMove={handleMouseMove}>
      <p>鼠标 X 坐标: {mouseX}</p>
      <p>鼠标 Y 坐标: {mouseY}</p>
    </div>
  );
};

export default MousePositionExample;

在上述代码中,当鼠标在页面上移动时,handleMouseMove 函数通过 e.clientXe.clientY 获取鼠标的坐标位置并更新到状态中,进而在页面上显示。

常用方法

preventDefault 方法

正如前面提到的,preventDefault 方法用于阻止事件的默认行为。除了表单提交和链接点击,它在很多场景下都有应用。比如在处理 contextmenu 事件时,阻止默认的右键菜单弹出。

import React from'react';

const PreventContextMenuExample = () => {
  const handleContextMenu = (e) => {
    e.preventDefault();
    console.log('右键菜单默认行为已阻止');
  };

  return (
    <div style={{ height: '100vh', width: '100vw' }} onContextMenu={handleContextMenu}>
      <p>在此区域右键点击,默认菜单将被阻止</p>
    </div>
  );
};

export default PreventContextMenuExample;

在这个例子中,当在指定区域右键点击时,handleContextMenu 函数中的 e.preventDefault() 阻止了默认的右键菜单弹出。

stopPropagation 方法

stopPropagation 方法在事件冒泡的场景下起着关键作用。它不仅可以阻止事件向上冒泡,还可以在一些复杂的组件嵌套结构中,避免不必要的父组件事件处理函数被触发。

import React from'react';

const NestedComponent = ({ handleClick }) => {
  return (
    <div>
      <button onClick={handleClick}>点击我</button>
    </div>
  );
};

const StopPropagationInNestedExample = () => {
  const handleInnerClick = (e) => {
    e.stopPropagation();
    console.log('内部组件点击,事件冒泡已阻止');
  };

  const handleOuterClick = () => {
    console.log('外部组件点击');
  };

  return (
    <div onClick={handleOuterClick}>
      <NestedComponent handleClick={handleInnerClick} />
    </div>
  );
};

export default StopPropagationInNestedExample;

在这个嵌套组件的例子中,内部组件的按钮点击事件通过 e.stopPropagation() 阻止了事件冒泡到外部组件,从而外部组件的点击处理函数不会被触发。

stopImmediatePropagation 方法

stopImmediatePropagation 方法与 stopPropagation 方法类似,但它更为严格。stopPropagation 方法只会阻止事件继续冒泡,而 stopImmediatePropagation 方法不仅阻止事件冒泡,还会阻止同一元素上其他事件处理函数的执行。

import React from'react';

const StopImmediatePropagationExample = () => {
  const handleFirstClick = (e) => {
    e.stopImmediatePropagation();
    console.log('第一个点击处理函数,事件传播已阻止');
  };

  const handleSecondClick = () => {
    console.log('第二个点击处理函数(不会被执行)');
  };

  return (
    <button onClick={handleFirstClick} onClick={handleSecondClick}>
      点击我
    </button>
  );
};

export default StopImmediatePropagationExample;

在上述代码中,当按钮被点击时,handleFirstClick 函数中的 e.stopImmediatePropagation() 不仅阻止了事件冒泡,还使得 handleSecondClick 函数不会被执行。

nativeEvent 属性与获取原生事件

虽然 React 使用合成事件来统一跨浏览器的事件处理,但在某些情况下,我们可能需要访问原生事件对象。通过事件对象的 nativeEvent 属性可以获取到原生事件。不过要谨慎使用,因为这会打破 React 事件系统的一致性和优势。

import React from'react';

const NativeEventExample = () => {
  const handleClick = (e) => {
    const nativeEvent = e.nativeEvent;
    console.log('原生事件对象:', nativeEvent);
  };

  return (
    <button onClick={handleClick}>点击获取原生事件</button>
  );
};

export default NativeEventExample;

在这个例子中,点击按钮时,通过 e.nativeEvent 获取到原生事件对象并打印到控制台,开发者可以进一步操作原生事件对象的属性和方法,但要注意兼容性等问题。

事件对象在不同类型事件中的应用

表单事件中的应用

在表单相关的事件,如 changesubmit 等事件中,事件对象提供了获取表单数据和控制表单行为的重要信息。

import React, { useState } from'react';

const FormEventExample = () => {
  const [inputValue, setInputValue] = useState('');

  const handleChange = (e) => {
    setInputValue(e.target.value);
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('提交的表单值:', inputValue);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" value={inputValue} onChange={handleChange} />
      <button type="submit">提交</button>
    </form>
  );
};

export default FormEventExample;

在这个表单例子中,change 事件通过 e.target.value 获取输入框的值并更新状态,submit 事件通过 e.preventDefault() 阻止表单默认提交行为,并获取当前输入框的值进行后续处理。

鼠标事件中的应用

鼠标事件如 clickmousedownmouseupmousemove 等,事件对象为实现各种鼠标交互效果提供了丰富的信息。例如,实现一个简单的拖拽效果。

import React, { useState } from'react';

const DragAndDropExample = () => {
  const [position, setPosition] = useState({ x: 0, y: 0 });
  const [isDragging, setIsDragging] = useState(false);
  const [startX, setStartX] = useState(0);
  const [startY, setStartY] = useState(0);

  const handleMouseDown = (e) => {
    setIsDragging(true);
    setStartX(e.clientX);
    setStartY(e.clientY);
  };

  const handleMouseMove = (e) => {
    if (isDragging) {
      const newX = e.clientX - startX + position.x;
      const newY = e.clientY - startY + position.y;
      setPosition({ x: newX, y: newY });
    }
  };

  const handleMouseUp = () => {
    setIsDragging(false);
  };

  return (
    <div
      style={{
        position: 'absolute',
        left: position.x,
        top: position.y,
        width: '100px',
        height: '100px',
        backgroundColor: 'lightblue',
        cursor: 'pointer'
      }}
      onMouseDown={handleMouseDown}
      onMouseMove={handleMouseMove}
      onMouseUp={handleMouseUp}
    >
      拖拽我
    </div>
  );
};

export default DragAndDropExample;

在这个拖拽例子中,mousedown 事件记录起始位置并标记开始拖拽,mousemove 事件根据鼠标移动距离更新元素位置,mouseup 事件标记拖拽结束。

键盘事件中的应用

键盘事件如 keydownkeyup 等常用于实现与键盘输入相关的功能,如快捷键操作、实时搜索等。

import React, { useState } from'react';

const KeyboardShortcutExample = () => {
  const [message, setMessage] = useState('');

  const handleKeyDown = (e) => {
    if (e.key === 'Enter') {
      setMessage('你按下了回车键');
    }
  };

  return (
    <div>
      <input type="text" onKeyDown={handleKeyDown} />
      <p>{message}</p>
    </div>
  );
};

export default KeyboardShortcutExample;

在这个例子中,当在输入框中按下回车键时,handleKeyDown 函数通过判断 e.key 是否为 Enter 来更新提示信息。

注意事项与最佳实践

  1. 事件委托:在 React 中,推荐使用事件委托来处理大量相似元素的事件。通过在父元素上绑定一个事件处理函数,利用事件冒泡机制,根据 event.target 判断具体触发事件的元素,这样可以减少事件处理函数的数量,提高性能。
  2. 避免内存泄漏:在使用 addEventListener 等原生事件绑定方式与 React 事件混合使用时,要确保在组件卸载时正确移除事件监听器,避免内存泄漏。可以在 componentWillUnmount 生命周期方法(对于类组件)或 useEffect 的返回函数(对于函数组件)中进行移除操作。
  3. 合理使用原生事件:虽然通过 nativeEvent 可以访问原生事件,但尽量优先使用 React 的合成事件。只有在确实需要原生事件的特定功能且合成事件无法满足时,才使用 nativeEvent,并且要注意跨浏览器兼容性。
  4. 事件处理函数的性能优化:如果事件处理函数内部有复杂的计算或操作,可以考虑使用 memoize 技术,如 useCallback 钩子(对于函数组件)来缓存事件处理函数,避免不必要的重新渲染。

总之,深入理解 React 事件对象的常用属性与方法,并遵循最佳实践,能够帮助开发者构建出更加高效、稳定且交互性良好的前端应用。在实际开发中,要根据具体的业务需求和场景,灵活运用这些知识,不断优化用户体验。