Skip to content

React 文档

导读与全书目录表:见同目录 01-React 入门:简介·特性与设计范式.md 开头。


React 的事件机制

回答

shell
React 并非直接使用浏览器原生事件,而是基于原生事件封装了一套合成事件(SyntheticEvent) 系统,目的是跨浏览器兼容、统一事件行为,同时优化性能。
React 事件的核心是合成事件(SyntheticEvent) + 事件委托,委托目标在 React 17 后从 document 改为组件根节点 如#root;
React 合成事件封装了原生事件,提供跨浏览器兼容。**React 17 起已移除事件池**,合成事件对象不再被复用;**React 16 及以前**在异步回调里访问事件字段需 **`e.persist()`**,否则可能读到被清空的对象。
事件执行顺序:原生捕获 → React 合成捕获 → React 合成冒泡 → 原生冒泡,手动绑定的原生事件需在组件卸载时解绑。

# 原生与合成事件对比
特性            原生 DOM 事件                     React 合成事件
绑定方式         element.addEventListener()      JSX 中直接写 onClick={handler}
事件对象         浏览器原生 Event 对象              React 封装的 SyntheticEvent 对象
事件委托目标      绑定在具体元素上                    统一委托到 document(React 17 后改为组件根节点)
跨浏览器兼容性    需要手动处理(如 IE 的 attachEvent) 内置兼容,无需关注浏览器差异

合成事件的使用与注意点

js
import React from 'react';

function Button() {
  const handleClick = (e) => {
    console.log(e.target);
    e.preventDefault();
    e.stopPropagation();

    // React 17+:无事件池,异步里仍可读取 e(若需长期保存,请自行拷贝字段或存 nativeEvent)
    setTimeout(() => {
      console.log(e.target);
    }, 1000);
  };

  return <button onClick={handleClick}>点击我</button>;
}

export default Button;

绑定事件&获取 this

在函数组件中

函数组件中无 this,回调函数的默认参数作为 $event

  • 方式一:绑定有参数的事件(类属性箭头函数)
js
// 虽然每次渲染都会创建新的函数实例,但是因为需要传参,所以在所难免
const handleClick = (count, $event) => {
  setCount((prevCount) => prevCount + count);
};
<div onClick={(e) => handleClick(100, e)}>点击</div>;
  • 方式二:绑定无参数的事件(函数引用)
js
// 优点是每次渲染都不会创建新的函数实例;无业务参数时 handler 无需形参
const handleClick = () => {
  setCount((prevCount) => prevCount + 1);
};
<div onClick={handleClick}>点击</div>;
  • 方式三:事件作为 props 传递给子组件(useCallback 优化)
js
// 通过依赖项缓存函数结果
const handleClick3 = useCallback((count, $event) => {
  setCount((prevCount) => prevCount + count);
}, []);
<Button onClick={(e) => handleClick3(1000, e)}>使用 useCallback 优化性能 ++ </Button>;

在类组件中

  • 方式一:绑定有参数的事件(类属性箭头函数)
js
// 缺点是每次 render 创建新箭头函数的问题
class MyComponent extends React.Component {
  handleClick = (id, $event) => {
    console.log('点击了', id);
    console.log('this', this); // 类属性箭头函数,自动绑定 this
    console.log('$event', $event);
  };

  render() {
    return <button onClick={(e) => this.handleClick(123, e)}>点击按钮</button>;
  }
}
  • 方式二:绑定无参数的事件(构造函数中绑定)
js
// 只绑定一次,性能最优
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    console.log('点击事件触发', this); // this 指向当前组件实例
  }

  render() {
    return <button onClick={this.handleClick}>点击我</button>;
  }
}
  • 方式三:事件作为 props 传递给子组件(无参使用方式二,有参数使用高阶函数 + 实例方法的方式)
js
class Parent extends React.Component {
  // 1. 定义接收参数的实例方法
  handleItemClick = (id) => {
    console.log('点击的 item id:', id);
  };

  // 2. 定义返回函数的高阶函数(仅创建一次)
  getHandleClick = (id) => {
    return () => this.handleItemClick(id);
  };

  render() {
    return (
      <div>
        {/* 3. 传递高阶函数的返回值,引用唯一 */}
        <Child onClick={this.getHandleClick(1)} />
        <Child onClick={this.getHandleClick(2)} />
      </div>
    );
  }
}

总结

非复杂场景下的绑定事件的方式优先使用:类属性箭头函数的方式,此法只是每次渲染会重新箭头函数,影响不大


react 表单控件

控件理解

  • 受控组件:表单元素的值由 React 控制,通过 value 绑定状态,通过 onChange 事件更新状态
  • 非受控组件:表单元素的值由 DOM 控制,通过 ref 获取表单控件的 DOM 元素,从而获取表单中的值,无法控制更新状态

如何选择

  • 受控组件(精细化):实时验证场景,实时交互场景
  • 非受控组件(简洁化,性能好):直接获取表单值并提交的简单场景

受控组件

  • 表单数据由 React 组件管理
  • 表单元素的值通过 value 或 checked 属性绑定到 state
  • 通过 onChange 等事件处理函数更新 state
  • 特点:完全控制表单数据、即时验证和反馈、有条件地禁用/启用按钮、强制输入格式
js
class ControlledForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = { value: '' };
  }

  handleChange = (event) => {
    this.setState({ value: event.target.value });
  }

  handleSubmit = (event) => {
    alert('提交的值: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          名字:
          <input 
            type="text" 
            value={this.state.value} 
            onChange={this.handleChange} 
          />
        </label>
        <button type="submit">提交</button>
      </form>
    );
  }
}

非受控组件

  • 非受控组件是指表单元素的值由 DOM 自身管理,而不是由 React 控制。
  • 特点:表单数据由 DOM 节点自身处理、使用 ref 来从 DOM 获取表单值
js
class UncontrolledForm extends React.Component {
  constructor(props) {
    super(props);
    this.inputRef = React.createRef();
  }

  handleSubmit = (event) => {
    alert('提交的值: ' + this.inputRef.current.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          名字:
          <input 
            type="text" 
            defaultValue="默认值" 
            ref={this.inputRef} 
          />
        </label>
        <button type="submit">提交</button>
      </form>
    );
  }
}

注意事项

shell
文件输入 (<input type="file" />) 始终是非受控组件,因为它的值只能由用户设置
 React 中,value 属性通常用于受控组件,而 defaultValue 用于非受控组件
对于复选框和单选按钮,受控组件使用 checked 属性,非受控组件使用 defaultChecked

受控组件和非受控组件的区别?

回答

shell
核心区别:数据由谁控制;
 受控组件:表单数据由 React 状态(useState)控制,输入 onChange 时更新状态,value 绑定状态;比如 <input value={val} onChange={(e) => setVal (e.target.value)} />
 非受控组件:表单数据由 DOM 自身控制,用 useRef 获取值,比如 <input ref={inputRef} />
场景:需要实时验证、联动的表单用受控;简单表单(比如文件上传)用非受控。