React函数组件与类组件:从疑惑到真香的心路历程

还记得刚开始学习React时,我总是在类组件和函数组件之间纠结。每次创建新组件都要面临选择困难症:是用传统的类组件,还是尝试新的函数组件?随着Hooks的推出,这种选择变得更加有趣。

今天我想和大家分享这段学习历程,希望能帮助正在面临同样选择的你。

初识类组件:面向对象的思维模式

我最初接触React时,类组件是绝对的主流。那时候的函数组件还被称为"无状态组件",功能相当有限。

jsx 复制代码
class Welcome extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
      username: '小明'
    };
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState({ count: this.state.count + 1 });
  }

  componentDidMount() {
    console.log('组件挂载完成!');
  }

  render() {
    return (
      <div>
        <h1>Hello, {this.state.username}!</h1>
        <p>点击次数: {this.state.count}</p>
        <button onClick={this.handleClick}>点击我</button>
      </div>
    );
  }
}

这种面向对象的写法对我来说很自然,特别是已经有其他面向对象语言经验的开发者。但随着时间的推移,我也发现了一些痛点:

函数组件的华丽转身

当React Hooks推出后,函数组件彻底改变了我的开发方式:

jsx 复制代码
function Welcome() {
  const [count, setCount] = useState(0);
  const [username, setUsername] = useState('小明');

  useEffect(() => {
    console.log('组件挂载完成!');
  }, []);

  const handleClick = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <h1>Hello, {username}!</h1>
      <p>点击次数: {count}</p>
      <button onClick={handleClick}>点击我</button>
    </div>
  );
}

这种写法让我眼前一亮!代码变得如此简洁,再也不用担心this的绑定问题了。

核心差异对比

1. 代码简洁性

类组件需要更多的样板代码:

jsx 复制代码
class UserProfile extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      user: null,
      loading: true
    };
  }

  async componentDidMount() {
    const user = await fetchUser(this.props.userId);
    this.setState({ user, loading: false });
  }

  componentDidUpdate(prevProps) {
    if (prevProps.userId !== this.props.userId) {
      this.setState({ loading: true });
      this.fetchUserData();
    }
  }

  fetchUserData = async () => {
    const user = await fetchUser(this.props.userId);
    this.setState({ user, loading: false });
  };

  render() {
    if (this.state.loading) return <div>加载中...</div>;
    return <div>{this.state.user.name}</div>;
  }
}

函数组件用Hooks实现同样的功能:

jsx 复制代码
function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchData = async () => {
      setLoading(true);
      const userData = await fetchUser(userId);
      setUser(userData);
      setLoading(false);
    };

    fetchData();
  }, [userId]);

  if (loading) return <div>加载中...</div>;
  return <div>{user.name}</div>;
}

2. 生命周期管理的不同思路

类组件的生命周期方法:

jsx 复制代码
class DataFetcher extends React.Component {
  componentDidMount() {
    // 组件挂载后执行
    this.startInterval();
  }

  componentDidUpdate(prevProps) {
    // props更新时执行
    if (prevProps.interval !== this.props.interval) {
      this.clearInterval();
      this.startInterval();
    }
  }

  componentWillUnmount() {
    // 组件卸载前清理
    this.clearInterval();
  }

  startInterval = () => {
    this.intervalId = setInterval(() => {
      this.fetchData();
    }, this.props.interval);
  };

  clearInterval = () => {
    clearInterval(this.intervalId);
  };

  fetchData = () => {
    // 获取数据逻辑
  };

  render() {
    return <div>定时器组件</div>;
  }
}

函数组件使用Effect Hook:

jsx 复制代码
function DataFetcher({ interval }) {
  useEffect(() => {
    const intervalId = setInterval(() => {
      fetchData();
    }, interval);

    // 清理函数
    return () => clearInterval(intervalId);
  }, [interval]); // 依赖数组

  const fetchData = () => {
    // 获取数据逻辑
  };

  return <div>定时器组件</div>;
}

实际开发中的体验差异

逻辑复用方式

在类组件时代,我们使用高阶组件或render props来复用逻辑:

jsx 复制代码
// 高阶组件方式
function withAuth(WrappedComponent) {
  return class extends React.Component {
    state = { user: null };
    
    componentDidMount() {
      this.checkAuth();
    }
    
    checkAuth = async () => {
      const user = await checkAuthentication();
      this.setState({ user });
    };
    
    render() {
      return <WrappedComponent user={this.state.user} {...this.props} />;
    }
  };
}

现在使用自定义Hook:

jsx 复制代码
// 自定义Hook
function useAuth() {
  const [user, setUser] = useState(null);
  
  useEffect(() => {
    const checkAuth = async () => {
      const userData = await checkAuthentication();
      setUser(userData);
    };
    
    checkAuth();
  }, []);
  
  return user;
}

// 在组件中使用
function UserProfile() {
  const user = useAuth();
  // ... 组件逻辑
}

性能考量

很多人担心函数组件的性能问题,但React团队做了大量优化。实际上,在现代React版本中,两者性能差异微乎其微。

jsx 复制代码
// React.memo优化函数组件
const OptimizedComponent = React.memo(function MyComponent({ data }) {
  // 组件逻辑
  return <div>{data}</div>;
});

// 类组件的PureComponent
class OptimizedClassComponent extends React.PureComponent {
  render() {
    return <div>{this.props.data}</div>;
  }
}

我的选择建议

经过多个项目的实践,我现在更倾向于使用函数组件:

  1. 新项目:毫不犹豫选择函数组件 + Hooks
  2. 老项目维护:根据具体情况,逐步迁移到函数组件
  3. 学习路径:新手可以直接从函数组件开始学习

但类组件仍然有其价值,特别是在维护老项目或某些特殊场景下。

结语

从类组件到函数组件的转变,反映了React生态的演进。函数组件以其简洁性和强大的Hooks能力,为React开发带来了新的活力。

⭐ 写在最后

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

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

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

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

✅ 解答我文章中一些疑问

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

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

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

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

相关推荐
2301_818732064 小时前
创建vue3项目,npm install后,运行报错,已解决
前端·npm·node.js
一支鱼4 小时前
从一个前端程序员的角度来看iPhone 17 与 iOS 26 的 Web 性能与交互革新
前端·ios·产品
前端Hardy4 小时前
HTML&CSS:高颜值歌词播放器
前端·javascript·css
Kisang.4 小时前
【HarmonyOS】HMRouter关键原理-动态import
前端·华为·typescript·harmonyos·鸿蒙
政采云技术4 小时前
解析 rc-field-form,探索 zero-form
前端
钱学敏4 小时前
Webpack 与 Gradle:构建工具的类比之旅
前端
用户4015442952614 小时前
vue 设置代理后,get请求正常,post请求报403
前端
李大玄5 小时前
ClipboardApp —— Mac 专属轻量级剪切板助手(开源)
前端·javascript·electron
bitbitDown5 小时前
如何优雅忽略 components.d.ts的更新
前端·javascript·vue.js