非受控组件在 React 中如何进行状态更新?

发布于:2025-05-17 ⋅ 阅读:(21) ⋅ 点赞:(0)

在 React 中,非受控组件的状态由 DOM 自身管理,React 不直接控制其值。但在某些场景下,仍需通过特定方式触发状态更新或与组件交互。以下是几种常见的实现方法:

一、使用 ref 获取最新值(提交时更新)

核心逻辑

通过 ref 直接访问 DOM 元素,在需要时(如提交表单)获取最新值。

示例代码
import { useRef } from 'react';

function LoginForm() {
  const usernameRef = useRef(null);
  const passwordRef = useRef(null);

  const handleSubmit = (e) => {
    e.preventDefault();
    // 通过 ref 获取 DOM 值
    const username = usernameRef.current.value;
    const password = passwordRef.current.value;
    console.log('提交登录:', { username, password });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        ref={usernameRef}
        type="text"
        placeholder="用户名"
        defaultValue="游客" // 初始值通过 defaultValue 设置
      />
      <input
        ref={passwordRef}
        type="password"
        placeholder="密码"
      />
      <button type="submit">登录</button>
    </form>
  );
}
适用场景
  • 简单表单(如登录、评论),仅需在提交时获取数据。
  • 无需实时响应输入变化的场景。

二、结合事件监听(特定时机更新)

核心逻辑

在特定事件(如 onChangeonBlur)中通过 ref 获取值并更新组件状态。

示例代码
import { useRef, useState } from 'react';

function EmailInput() {
  const inputRef = useRef(null);
  const [email, setEmail] = useState('');
  const [isValid, setIsValid] = useState(true);

  const handleBlur = () => {
    const value = inputRef.current.value;
    setEmail(value);
    setIsValid(value.includes('@')); // 简单验证
  };

  return (
    <div>
      <input
        ref={inputRef}
        type="email"
        onBlur={handleBlur}
        placeholder="输入邮箱"
      />
      {!isValid && <span style={{ color: 'red' }}>邮箱格式不正确</span>}
    </div>
  );
}
适用场景
  • 需要在用户离开输入框时验证数据。
  • 部分状态需要与 DOM 值同步,但无需实时更新。

三、使用 ref 触发 DOM 方法(间接更新)

核心逻辑

通过 ref 调用 DOM 原生方法(如 focus()select())间接影响用户输入。

示例代码
import { useRef } from 'react';

function AutoFocusInput() {
  const inputRef = useRef(null);

  // 组件挂载后自动聚焦
  React.useEffect(() => {
    inputRef.current.focus();
  }, []);

  const handleSelectAll = () => {
    inputRef.current.select(); // 选中文本
  };

  return (
    <div>
      <input ref={inputRef} type="text" defaultValue="全选我" />
      <button onClick={handleSelectAll}>全选</button>
    </div>
  );
}
适用场景
  • 自动聚焦、文本选择、滚动定位等交互需求。
  • 触发文件选择对话框(<input type="file" />)。

四、与受控状态混合使用

核心逻辑

部分字段使用受控模式(实时响应),部分使用非受控模式(减少状态管理)。

示例代码
import { useRef, useState } from 'react';

function MixedForm() {
  // 受控字段:实时验证
  const [username, setUsername] = useState('');
  const [usernameError, setUsernameError] = useState('');

  // 非受控字段:仅提交时获取
  const passwordRef = useRef(null);

  const handleUsernameChange = (e) => {
    const value = e.target.value;
    setUsername(value);
    setUsernameError(value.length < 3 ? '用户名至少3个字符' : '');
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    const password = passwordRef.current.value;
    console.log('提交表单:', { username, password });
  };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <input
          type="text"
          value={username}
          onChange={handleUsernameChange}
          placeholder="用户名(受控)"
        />
        {usernameError && <span style={{ color: 'red' }}>{usernameError}</span>}
      </div>
      <div>
        <input
          ref={passwordRef}
          type="password"
          placeholder="密码(非受控)"
        />
      </div>
      <button type="submit">提交</button>
    </form>
  );
}
适用场景
  • 复杂表单中,部分字段需要实时响应,部分仅需最终值。
  • 平衡性能与开发复杂度。

五、注意事项

  1. 初始值设置

    • 非受控组件使用 defaultValue 或 defaultChecked 设置初始值(仅首次渲染有效)。
    <input type="text" ref={inputRef} defaultValue="初始值" />
    
  2. 避免混用受控与非受控属性

    • 不要同时设置 value 和 ref,否则会导致 React 警告。
    // 错误:同时使用 value(受控)和 ref(非受控)
    <input value={value} ref={inputRef} />
    
  3. 性能考量

    • 非受控组件在大规模表单中可减少状态更新开销,但需注意 ref 的内存管理(避免泄漏)。

总结:非受控组件的状态更新策略

需求类型 实现方式 示例场景
提交时获取最终值 使用 ref.current.value 登录表单、评论提交
特定时机验证 结合事件监听(如 onBlur 邮箱格式验证、字数统计
触发 DOM 交互 通过 ref 调用原生方法 自动聚焦、文件选择对话框
混合模式 部分字段受控,部分非受控 复杂表单中平衡性能与状态管理

非受控组件的核心优势在于简化状态管理,适合不需要实时响应的场景。通过合理使用 ref 和事件监听,可在保持简洁的同时满足特定的交互需求。