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

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

相关推荐
BillKu3 分钟前
Vue3组件加载顺序
前端·javascript·vue.js
IT_陈寒11 分钟前
Python性能优化必知必会:7个让代码快3倍的底层技巧与实战案例
前端·人工智能·后端
暖木生晖23 分钟前
引入资源即针对于不同的屏幕尺寸,调用不同的css文件
前端·css·媒体查询
袁煦丞1 小时前
DS file文件管家远程自由:cpolar内网穿透实验室第492个成功挑战
前端·程序员·远程工作
用户013741284371 小时前
九个鲜为人知却极具威力的 CSS 功能:提升前端开发体验的隐藏技巧
前端
永远不打烊1 小时前
Window环境 WebRTC demo 运行
前端
风舞1 小时前
一文搞定JS所有类型判断最佳实践
前端·javascript
coding随想1 小时前
哈希值变化的魔法:深入解析HTML5 hashchange事件的奥秘与实战
前端
一树山茶1 小时前
uniapp在微信小程序中实现 SSE进行通信
前端·javascript
coding随想1 小时前
小程序中的pageshow与pagehide事件,HTML5中也有?揭秘浏览器往返缓存(BFCache)
前端