Class 组件的局限性
React Class 组件存在的问题及 Hooks 如何解决
问题
React Class 组件存在哪些问题?为什么 React 团队推出了 Hooks?
解答
1. this 绑定繁琐
Class 组件中事件处理函数需要手动绑定 this,否则会丢失上下文。
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
// 方式1:构造函数中绑定
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({ count: this.state.count + 1 });
}
// 方式2:箭头函数(类字段语法)
handleReset = () => {
this.setState({ count: 0 });
};
render() {
return (
<div>
<p>{this.state.count}</p>
<button onClick={this.handleClick}>+1</button>
{/* 方式3:内联箭头函数(每次渲染创建新函数) */}
<button onClick={() => this.handleReset()}>Reset</button>
</div>
);
}
}
使用 Hooks 后完全不需要考虑 this:
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}
2. 逻辑复用困难
Class 组件复用状态逻辑需要 HOC 或 render props,容易形成嵌套地狱。
// HOC 方式:多层嵌套
const EnhancedComponent = withRouter(
withTheme(
withAuth(
withLogging(MyComponent)
)
)
);
// render props 方式:回调地狱
<ThemeContext.Consumer>
{theme => (
<AuthContext.Consumer>
{auth => (
<MouseTracker>
{mouse => (
<MyComponent theme={theme} auth={auth} mouse={mouse} />
)}
</MouseTracker>
)}
</AuthContext.Consumer>
)}
</ThemeContext.Consumer>
Hooks 通过自定义 Hook 实现扁平化复用:
function MyComponent() {
const theme = useTheme();
const auth = useAuth();
const mouse = useMouse();
// 逻辑清晰,无嵌套
return <div>...</div>;
}
3. 生命周期分散相关逻辑
同一个功能的代码被拆分到不同生命周期中,难以维护。
class UserProfile extends React.Component {
state = { user: null, posts: [] };
componentDidMount() {
// 订阅用户数据
this.userSubscription = subscribeToUser(this.props.userId, user => {
this.setState({ user });
});
// 获取文章(同一生命周期混杂不相关逻辑)
fetchPosts(this.props.userId).then(posts => {
this.setState({ posts });
});
}
componentDidUpdate(prevProps) {
// 用户变化时重新订阅
if (prevProps.userId !== this.props.userId) {
this.userSubscription.unsubscribe();
this.userSubscription = subscribeToUser(this.props.userId, user => {
this.setState({ user });
});
fetchPosts(this.props.userId).then(posts => {
this.setState({ posts });
});
}
}
componentWillUnmount() {
// 清理订阅
this.userSubscription.unsubscribe();
}
render() {
return <div>...</div>;
}
}
Hooks 按功能组织代码:
function UserProfile({ userId }) {
// 用户相关逻辑聚合在一起
const [user, setUser] = useState(null);
useEffect(() => {
const subscription = subscribeToUser(userId, setUser);
return () => subscription.unsubscribe();
}, [userId]);
// 文章相关逻辑聚合在一起
const [posts, setPosts] = useState([]);
useEffect(() => {
fetchPosts(userId).then(setPosts);
}, [userId]);
return <div>...</div>;
}
4. 样板代码冗余
Class 组件需要大量样板代码:
// Class 组件:约 20 行
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = { on: false };
this.toggle = this.toggle.bind(this);
}
toggle() {
this.setState(state => ({ on: !state.on }));
}
render() {
return (
<button onClick={this.toggle}>
{this.state.on ? 'ON' : 'OFF'}
</button>
);
}
}
// 函数组件:约 8 行
function Toggle() {
const [on, setOn] = useState(false);
return (
<button onClick={() => setOn(!on)}>
{on ? 'ON' : 'OFF'}
</button>
);
}
5. 难以优化
- Class 不能很好地压缩(方法名不能被 minify)
- 热重载不稳定
- 预编译优化困难(如 component folding)
关键点
- this 绑定:Class 需要手动绑定,Hooks 无此问题
- 逻辑复用:HOC/render props 导致嵌套,自定义 Hook 实现扁平复用
- 代码组织:生命周期按时机划分,Hooks 按功能聚合
- 代码量:Class 样板代码多,函数组件更简洁
- 编译优化:函数组件更易被工具链优化
目录