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

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

相关推荐
雪隐8 分钟前
用Flutter做背单词APP-01服务器?不存在的,我家里就有
前端·人工智能·后端
用户22046039586841 分钟前
ES模块和commonJS两种标准下中实现__dirname?一篇文章教你理清楚
前端
犹豫的大炮1 小时前
jQuery最核心的基础设施之一——数据缓存模块进化史
前端·缓存·jquery
小碗细面1 小时前
从 PRD 到上线:SpecKit 如何让 AI 真正参与前端交付
前端·ai编程·claude
WL学习笔记1 小时前
链式队列(数据结构)
前端·数据结构·jquery
我是大卫1 小时前
React源码解析-第三部分:协调与提交
前端·react.js·源码
都想学1 小时前
我把 12 个在线工具关掉了,换成了一个 Chrome 扩展
前端·chrome
xiaogai_gai2 小时前
钉钉数据集成到金蝶云星空案例分享:传给钉钉后,回传金蝶字段
java·前端·钉钉
步步为营DotNet2 小时前
借助 Semantic Kernel 与 Blazor 构建智能交互式前端应用
前端
神奇的代码在哪里2 小时前
AI编程时代,PPT已成为过去,单个HTML文件创造无限可能
javascript·html·powerpoint·ai编程·ppt