单一指责原则
就一个类而言,应该仅有一个引起它变化的原因。在 JavaScript 中,需要用到类的场景并不太多,单一职责原则更多地是被运用在对象或者方法级别上,因此本节我们的讨论大多基于对象和方法。
因此,SRP 原则体现为:一个对象(方法)只做一件事情。
SRP 原则在很多设计模式中都有着广泛的运用,例如代理模式、迭代器模式、单例模式和装饰者模式。
优缺点
SRP 原则的优点是降低了单个类或者对象的复杂度,按照职责把对象分解成更小的粒度,这有助于代码的复用,也有利于进行单元测试。当一个职责需要变更的时候,不会影响到其他的职责。
但 SRP 原则也有一些缺点,最明显的是会增加编写代码的复杂度。当我们按照职责把对象分解成更小的粒度之后,实际上也增大了这些对象之间相互联系的难度。
React中的单一职责原则
常见的错误案例
这里有一个常见的反模式:
// DON'T DO THIS
const UserProfile = () => {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
fetchUser();
}, []);
const fetchUser = async () => {
try {
const response = await fetch("/api/user");
const data = await response.json();
setUser(data);
} catch (e) {
setError(e as Error);
} finally {
setLoading(false);
}
};
const handleUpdateProfile = async (data: Partial<User>) => {
try {
await fetch("/api/user", {
method: "PUT",
body: JSON.stringify(data),
});
fetchUser(); // Refresh data
} catch (e) {
setError(e as Error);
}
};
if (loading) return <div>Loading…</div>;
if (error) return <div>Error: {error.message}</div>;
if (!user) return <div>No user found</div>;
return (
<div>
<h1>{user.name}</h1>
<form onSubmit={/* form logic */}>{/* Complex form fields */}</form>
<UserStats userId={user.id} />
<UserPosts userId={user.id} />
</div>
);
};更好的方式:关注点分离
让我们将其拆分为专注的组件:
// 数据获取钩子
const useUser = (userId: string) => {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
fetchUser();
}, [userId]);
const fetchUser = async () => {
try {
const response = await fetch(`/api/user/${userId}`);
const data = await response.json();
setUser(data);
} catch (e) {
setError(e as Error);
} finally {
setLoading(false);
}
};
return { user, loading, error, refetch: fetchUser };
};
// 展示组件
const UserProfileView = ({
user,
onUpdate,
}: {
user: User;
onUpdate: (data: Partial<User>) => void;
}) => (
<div>
<h1>{user.name}</h1>
<UserProfileForm user={user} onSubmit={onUpdate} />
<UserStats userId={user.id} />
<UserPosts userId={user.id} />
</div>
);
// 容器组件
const UserProfileContainer = ({ userId }: { userId: string }) => {
const { user, loading, error, refetch } = useUser(userId);
const handleUpdate = async (data: Partial<User>) => {
try {
await fetch(`/api/user/${userId}`, {
method: "PUT",
body: JSON.stringify(data),
});
refetch();
} catch (e) {
// 错误处理
}
};
if (loading) return <LoadingSpinner />;
if (error) return <ErrorMessage error={error} />;
if (!user) return <NotFound message="User not found" />;
return <UserProfileView user={user} onUpdate={handleUpdate} />;
};关键要点
- 分离数据和展示 – 使用钩子处理数据,使用组件处理 UI
- 创建专注的组件 – 每个组件应该做好一件事
- 使用组合构建复杂功能的简单部分
- 将可复用逻辑 提取到自定义 hooks 中
- 分层思考 – 数据层、业务逻辑层、展示层