React 受控组件和非受控组件区别和使用场景

受控组件(Controlled Components)

定义: 受控组件是指表单数据由 React 组件状态(state)管理的组件。表单元素的值由 React 通过 value 或 checked 属性控制,并通过 onChange 等事件处理程序更新状态。

代码的的表现形式:

js 复制代码
import React, { Component } from "react";
import "./App.css";

export default class Form extends Component {
  state = {
    userName: "",
    gender: "男",
    hobby: [],
    intro: "",
    provinces: "",
  };

  changeUserName(e) {
    this.setState({
      userName: e.target.value,
    });
  }

  changeGender(gender) {
    this.setState({
      gender,
    });
  }

  changeHobby(currHobby) {
    let { hobby } = this.state;
    if (hobby.includes(currHobby)) {
      hobby = hobby.filter((item) => item !== currHobby);
    } else {
      hobby.push(currHobby);
    }
    this.setState({
      hobby,
    });
  }

  changeProvinces(e) {
    this.setState({
      provinces: e.target.value,
    });
  }

  changeIntro(e) {
    this.setState({
      intro: e.target.value,
    });
  }

  submitForm(e) {
    e.preventDefault();
    const data = { ...this.state };
    // 提交后台
  }

  render() {
    const { userName, gender, hobby, intro, provinces } = this.state;
    return (
      <form>
        <div className="form-item">
          <label>用户名:</label>
          <input value={userName} onChange={this.changeUserName.bind(this)} />
        </div>
        <div className="form-item">
          <label>性别:</label>
          <input
            type="radio"
            checked={gender === "男"}
            name="gender"
            onChange={this.changeGender.bind(this, "男")}
          />
          男
          <input
            type="radio"
            checked={gender === "女"}
            name="gender"
            onChange={this.changeGender.bind(this, "女")}
          />
          女
        </div>
        <div className="form-item">
          <label>爱好:</label>
          <input
            type="checkbox"
            checked={hobby.includes("跑步")}
            onChange={this.changeHobby.bind(this, "跑步")}
          />
          跑步
          <input
            type="checkbox"
            checked={hobby.includes("打篮球")}
            onChange={this.changeHobby.bind(this, "打篮球")}
          />
          打篮球
          <input
            type="checkbox"
            checked={hobby.includes("旅游")}
            onChange={this.changeHobby.bind(this, "旅游")}
          />
          旅游
          <input
            type="checkbox"
            checked={hobby.includes("打游戏")}
            onChange={this.changeHobby.bind(this, "打游戏")}
          />
          打游戏
        </div>
        <div className="form-item">
          <label>所在省:</label>
          <select value={provinces} onChange={this.changeProvinces.bind(this)}>
            <option>广东省</option>
            <option>江苏省</option>
            <option>山东省</option>
          </select>
        </div>
        <div className="form-item">
          <label>自我介绍:</label>
          <textarea
            value={intro}
            onChange={this.changeIntro.bind(this)}
          ></textarea>
        </div>
        <div className="form-item">
          <label>自我介绍:</label>
          <button onClick={this.submitForm.bind(this)}>提交</button>
        </div>
      </form>
    );
  }
}

当表单中的数据变化,对应的onChange事件就会监听到,从而更新状态(state),提交的时候就可以直接使用state上的数据。效果如下:

需要注意的是受控组件必须要有value 和onChange或者cheched和checked,少了一个可能会输入不正常,比如你将userName的上面的onchange去掉,你会发现输入框输入不了东西。

非受控组件 (Uncontrolled Components)

定义: 非受控组件是指表单数据由 DOM 自身管理的组件。你可以使用 ref 来从 DOM 中获取表单值,而不是为每个状态更新编写事件处理程序。

js 复制代码
import React, { Component, createRef } from "react";
import "./App.css";

export default class Form extends Component {
  constructor() {
    super();
    this.userNameRef = createRef();

    this.maleRef = createRef();
    this.femaleRef = createRef();

    this.runRef = createRef();
    this.basketballRef = createRef();
    this.travelRef = createRef();
    this.gameRef = createRef();

    this.provincesRef = createRef();

    this.introRef = createRef();
  }

  componentDidMount() {}
  submitForm(e) {
    e.preventDefault();
    // 获取用户名
    const userName = this.userNameRef.current.value;
    console.log("用户名:" + userName);
    // 处理性别单选框
    const gender = [this.maleRef.current, this.femaleRef.current].filter(
      (item) => item.checked
    )[0].value;
    console.log("性别:" + gender);

    // 处理爱好复选框
    const hobby = [
      this.runRef.current,
      this.basketballRef.current,
      this.travelRef.current,
      this.gameRef.current,
    ]
      .filter((item) => {
        return item.checked;
      })
      .map((item) => item.value);
    console.log("爱好" + hobby);

    // 获取省份
    const provinces = this.provincesRef.current.value;
    console.log("省份:" + provinces);

    // 获取自我介绍
    const intro = this.introRef.current.value;
    console.log("自我介绍:" + intro);

    // 提交数据
    const data = {
      userName,
      gender,
      hobby,
      provinces,
      intro,
    };
    console.log(JSON.stringify(data));
  }
  render() {
    return (
      <form>
        <div className="form-item">
          <label>用户名:</label>
          <input ref={this.userNameRef} />
        </div>
        <div className="form-item">
          <label>性别:</label>
          <input type="radio" name="gender" ref={this.maleRef} value="男" />
          男
          <input type="radio" name="gender" ref={this.femaleRef} value="女" />女
        </div>
        <div className="form-item">
          <label>爱好:</label>
          <input type="checkbox" value="跑步" ref={this.runRef} />
          跑步
          <input type="checkbox" value="打篮球" ref={this.basketballRef} />
          打篮球
          <input type="checkbox" value="旅游" ref={this.travelRef} />
          旅游
          <input type="checkbox" value="打游戏" ref={this.gameRef} />
          打游戏
        </div>
        <div className="form-item">
          <label>所在省:</label>
          <select ref={this.provincesRef}>
            <option>广东省</option>
            <option>江苏省</option>
            <option>山东省</option>
          </select>
        </div>
        <div className="form-item">
          <label>自我介绍:</label>
          <textarea ref={this.introRef}></textarea>
        </div>
        <div className="form-item">
          <label>自我介绍:</label>
          <button onClick={this.submitForm.bind(this)}>提交</button>
        </div>
      </form>
    );
  }
}

以上代码可以看出,非受控组件在处理单选框和复选框时会非常麻烦。另外还有一个注意点2个注意点:

  1. 想文本输入框有默认值使用defaultValue进行设置,不能使用value进行设置,否则控制会给出警告 ,例如下面代码
js 复制代码
<div className="form-item">
  <label>用户名:</label>
  <input ref={this.userNameRef} value="hello" />
</div>

控制台:

  1. 想要复选框,单选框默认选中使用defaultChecked进行设置,不能使用cheched进行设置,否则控制也会给出警告,例如下面的代码:
js 复制代码
<div className="form-item">
  <label>爱好:</label>
  <input type="checkbox" checked value="跑步" ref={this.runRef} />
  跑步
  <input type="checkbox" value="打篮球" ref={this.basketballRef} />
  打篮球
  <input type="checkbox" value="旅游" ref={this.travelRef} />
  旅游
  <input type="checkbox" value="打游戏" ref={this.gameRef} />
  打游戏
</div>

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

特性 受控组件 非受控组件
数据管理 由 React 状态管理 由 DOM 自身管理
值控制 通过 value/checked 属性 通过 ref 获取值
变化处理 需要 onChange 处理程序 不需要事件处理程序
即时验证 容易实现 较难实现
性能 每次输入都触发渲染 不触发额外渲染
表单提交 可直接从状态获取数据 需要通过 ref 或事件获取数据

使用场景

受控组件使用场景

需要即时验证或反馈: 例如实时显示输入是否有效

需要根据输入禁用/启用按钮: 如提交按钮在表单有效前禁用

强制特定输入格式: 如强制大写或格式化输入

多个输入相互依赖: 一个输入的值影响另一个输入

动态表单: 根据用户输入动态添加/删除表单字段

使用非受控组件的场景

简单表单,不需要即时验证: 只需在提交时获取值

文件输入: <input type="file"> 总是非受控的

与第三方库集成: 需要直接操作 DOM

性能敏感场景: 避免每次输入都触发渲染

快速原型开发: 不需要复杂逻辑的简单表单

最佳实践

  1. 大多数情况下推荐使用受控组件,因为它们提供了更好的控制和一致性

  2. 对于性能关键的表单,考虑使用非受控组件或优化受控组件(如防抖)

  3. 文件输入必须使用非受控组件,因为它是只读的

今天就分享到这里了感谢收看

相关推荐
sasaraku.7 分钟前
serviceWorker缓存资源
前端
RadiumAg1 小时前
记一道有趣的面试题
前端·javascript
yangzhi_emo1 小时前
ES6笔记2
开发语言·前端·javascript
yanlele1 小时前
我用爬虫抓取了 25 年 5 月掘金热门面试文章
前端·javascript·面试
中微子3 小时前
React状态管理最佳实践
前端
烛阴3 小时前
void 0 的奥秘:解锁 JavaScript 中 undefined 的正确打开方式
前端·javascript
中微子3 小时前
JavaScript 事件与 React 合成事件完全指南:从入门到精通
前端
Hexene...3 小时前
【前端Vue】如何实现echarts图表根据父元素宽度自适应大小
前端·vue.js·echarts
初遇你时动了情3 小时前
腾讯地图 vue3 使用 封装 地图组件
javascript·vue.js·腾讯地图
dssxyz3 小时前
uniapp打包微信小程序主包过大问题_uniapp 微信小程序时主包太大和vendor.js过大
javascript·微信小程序·uni-app