React组件三剑客:高阶/受控/非受控组件的奇妙世界

大家好,我是小杨。今天想和大家聊聊React开发中三个特别重要但又容易混淆的概念:高阶组件、受控组件和非受控组件。记得我刚学React时,经常被这几个名词搞得头晕眼花,直到在实际项目中踩了不少坑,才真正理解它们的区别和用法。

通过这篇文章,我会用最接地气的方式,带你彻底搞懂这三个"组件兄弟",保证让你看完后不再confused!


一、先来认识一下这三兄弟

简单来说:

  • 高阶组件(HOC) :像是组件的"包装纸",用来增强组件功能
  • 受控组件(Controlled Component) :表单数据由React state完全控制
  • 非受控组件(Uncontrolled Component) :表单数据由DOM自身管理

下面我分别用代码示例来展示它们的用法和区别。


二、高阶组件:组件的超级装饰器

高阶组件其实就是一个函数,它接收一个组件,返回一个新的增强版组件。我最常用它来做代码复用和逻辑抽象。

来看个简单的例子:我要给多个组件添加用户认证功能

javascript 复制代码
// 高阶组件:withAuth
const withAuth = (WrappedComponent) => {
  return class AuthenticatedComponent extends React.Component {
    state = {
      isAuthenticated: false
    };

    componentDidMount() {
      // 模拟检查用户登录状态
      const userToken = localStorage.getItem('userToken');
      this.setState({ isAuthenticated: !!userToken });
    }

    render() {
      if (!this.state.isAuthenticated) {
        return <div>请先登录!</div>;
      }
      
      return <WrappedComponent {...this.props} />;
    }
  };
};

// 使用高阶组件
const UserProfile = ({ userName }) => {
  return <div>欢迎, {userName}!</div>;
};

const AuthenticatedUserProfile = withAuth(UserProfile);

// 在App中使用
const App = () => {
  return (
    <div>
      <AuthenticatedUserProfile userName="小杨" />
    </div>
  );
};

高阶组件的好处是:逻辑复用,我可以在多个组件间共享认证逻辑,而不需要重复代码。


三、受控组件:React的乖孩子

受控组件的意思是:表单数据完全由React的state来控制。每次用户输入时,都会触发state更新,然后重新渲染组件。

这是我平时最常用的方式:

javascript 复制代码
class LoginForm extends React.Component {
  state = {
    username: '',
    password: ''
  };

  handleInputChange = (event) => {
    const { name, value } = event.target;
    this.setState({
      [name]: value
    });
  };

  handleSubmit = (event) => {
    event.preventDefault();
    console.log('提交的数据:', this.state);
  };

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <input
          type="text"
          name="username"
          value={this.state.username}
          onChange={this.handleInputChange}
          placeholder="用户名"
        />
        <input
          type="password"
          name="password"
          value={this.state.password}
          onChange={this.handleInputChange}
          placeholder="密码"
        />
        <button type="submit">登录</button>
      </form>
    );
  }
}

受控组件的特点

  • 数据完全由React管理
  • 实时验证和处理用户输入
  • 适合复杂的表单逻辑

四、非受控组件:让DOM自己玩

非受控组件则是让DOM自己管理数据,我们只是在需要的时候(比如提交时)去获取数据。

来看个例子:

javascript 复制代码
class UncontrolledForm extends React.Component {
  constructor(props) {
    super(props);
    this.usernameRef = React.createRef();
    this.passwordRef = React.createRef();
  }

  handleSubmit = (event) => {
    event.preventDefault();
    const formData = {
      username: this.usernameRef.current.value,
      password: this.passwordRef.current.value
    };
    console.log('提交的数据:', formData);
  };

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <input
          type="text"
          ref={this.usernameRef}
          placeholder="用户名"
        />
        <input
          type="password"
          ref={this.passwordRef}
          placeholder="密码"
        />
        <button type="submit">登录</button>
      </form>
    );
  }
}

非受控组件的适用场景

  • 表单很简单,不需要实时验证
  • 文件上传(<input type="file">
  • 只是想简单获取用户输入,不需要复杂处理

五、三兄弟的对比总结

为了更直观,我做了个对比表格:

特性 高阶组件 受控组件 非受控组件
用途 逻辑复用 表单控制 简单表单
数据管理 增强组件 React State DOM自身
实时响应 取决于实现 实时更新 提交时获取
代码量 较多 中等 较少

六、实际项目中的选择建议

根据我的经验:

  1. 用高阶组件当你需要在多个组件间共享逻辑时
  2. 用受控组件处理需要实时验证、复杂交互的表单
  3. 用非受控组件处理简单表单或者文件上传

有时候我也会混合使用:

javascript 复制代码
// 混合使用示例
class HybridForm extends React.Component {
  state = {
    email: ''
  };

  fileRef = React.createRef();

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

  handleSubmit = (event) => {
    event.preventDefault();
    const formData = {
      email: this.state.email,
      resume: this.fileRef.current.files[0]
    };
    console.log('混合表单数据:', formData);
  };

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        {/* 受控组件 */}
        <input
          type="email"
          value={this.state.email}
          onChange={this.handleEmailChange}
          placeholder="邮箱"
        />
        
        {/* 非受控组件 */}
        <input
          type="file"
          ref={this.fileRef}
        />
        
        <button type="submit">提交</button>
      </form>
    );
  }
}

七、最后的小贴士

  1. 高阶组件记得要传递props,避免"props丢失"问题
  2. 受控组件要注意性能,频繁的setState可能导致重渲染
  3. 非受控组件虽然简单,但失去了React的数据控制优势

理解这三个概念后,你在React开发中就能更加得心应手了。记住,没有绝对的好坏,只有适合的场景。

⭐ 写在最后

请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.

✅ 认为我某个部分的设计过于繁琐,有更加简单或者更高逼格的封装方式

✅ 认为我部分代码过于老旧,可以提供新的API或最新语法

✅ 对于文章中部分内容不理解

✅ 解答我文章中一些疑问

✅ 认为某些交互,功能需要优化,发现BUG

✅ 想要添加新功能,对于整体的设计,外观有更好的建议

✅ 一起探讨技术加qq交流群:906392632

最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧!

相关推荐
hj5914_前端新手2 小时前
javascript基础- 函数中 this 指向、call、apply、bind
前端·javascript
薛定谔的算法2 小时前
低代码编辑器项目设计与实现:以JSON为核心的数据驱动架构
前端·react.js·前端框架
Hilaku3 小时前
都2025年了,我们还有必要为了兼容性,去写那么多polyfill吗?
前端·javascript·css
yangcode3 小时前
iOS 苹果内购 Storekit 2
前端
LuckySusu3 小时前
【js篇】JavaScript 原型修改 vs 重写:深入理解 constructor的指向问题
前端·javascript
LuckySusu3 小时前
【js篇】如何准确获取对象自身的属性?hasOwnProperty深度解析
前端·javascript
LuckySusu3 小时前
【js篇】深入理解 JavaScript 作用域与作用域链
前端·javascript
LuckySusu3 小时前
【js篇】call() 与 apply()深度对比
前端·javascript
LuckySusu3 小时前
【js篇】addEventListener()方法的参数和使用
前端·javascript
该用户已不存在3 小时前
6个值得收藏的.NET ORM 框架
前端·后端·.net