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

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

相关推荐
Cat God 0075 小时前
完整静态工具网站(尝试)
前端·html
kaka-3335 小时前
微信小程序中使用 xlsx(xlsx.mini.min.js)实现 Excel 导入导出功能
javascript·微信小程序·excel
WindrunnerMax5 小时前
从零实现富文本编辑器#9-编辑器文本结构变更的受控处理
前端·架构·github
北冥有一鲲5 小时前
LangChain.js:Tool、Memory 与 Agent 的深度解析与实战
开发语言·javascript·langchain
Mintopia5 小时前
静态内容页该用HTML还是Next.js展示更好
前端·html·next.js
LYFlied6 小时前
【每日算法】LeetCode 226. 翻转二叉树
前端·算法·leetcode·面试·职场和发展
无名无姓某罗6 小时前
jQuery 请求 SpringMVC 接口返回404错误排查
前端·spring·jquery
霁月的小屋6 小时前
Vue响应式数据全解析:从Vue2到Vue3,ref与reactive的实战指南
前端·javascript·vue.js
李少兄6 小时前
深入理解 Java Web 开发中的 HttpServletRequest 与 HttpServletResponse
java·开发语言·前端
holidaypenguin6 小时前
antd 5 + react 18 + vite 7 升级
前端·react.js