事实是,组件看似简单。上手很容易——定义一个函数,返回一些 JSX,然后就到此为止。但是要编写干净、可维护且易于使用的组件吗?这是一场完全不同的比赛。
在没有意识到的情况下,我们创建了以下组件:
在这篇文章中,我将引导您了解开发人员在使用 React 组件时最常见的错误。更重要的是,我将向您展示如何在不破坏整个应用程序的情况下修复它们。
无论您是刚刚入门还是拥有多年的经验,这些技巧都将帮助您编写出不仅具有功能性而且易于维护的组件。
让我们来谈谈我们都犯过的经典错误:一切组件。您已经看到它了——它一开始很小而且天真无邪,可能是一个简单的表单或仪表板。快进一点,现在它正在管理状态、处理 API 调用、格式化数据,还可能为您冲泡早晨咖啡。
// Please, no more of this const UserDashboard = () => { const [userData, setUserData] = useState(null); const [orders, setOrders] = useState([]); const [notifications, setNotifications] = useState([]); const [settings, setSettings] = useState({}); const [isEditing, setIsEditing] = useState(false); const [activeTab, setActiveTab] = useState('profile'); // 15 separate useEffects // 10 different event handlers // Multiple conditional renders // 300+ lines of chaos };
听起来很熟悉吗?
如果出现以下情况,您的组件可能已变成“一切组件”:
解决方案?不要使用单一的所有组件,而是将职责分解为更小的、更有针对性的部分。
// A cleaner, smarter approach const UserDashboard = () => { return ( <div> <UserProfile /> <OrderHistory /> <NotificationCenter /> <UserSettings /> </div> ); };
重构时,不要根据组件在屏幕上的外观来破坏它们。按责任划分他们。问问自己:这个功能是否值得拥有自己的组件?如果它正在处理一些不同的东西——比如用户个人资料或订单历史记录——它可能会这样做。
提示: 一个好的组件只做一件事并且做得很好。如果您很难用一句话描述它的目的,那么它很可能试图做太多事情。
我们来讨论一下不太好玩的“传递道具”游戏。如果您曾将同一个 prop 通过多个组件传递给一个深度嵌套的子组件,那么您就陷入了 prop 钻探地狱。
// Please, no more of this const UserDashboard = () => { const [userData, setUserData] = useState(null); const [orders, setOrders] = useState([]); const [notifications, setNotifications] = useState([]); const [settings, setSettings] = useState({}); const [isEditing, setIsEditing] = useState(false); const [activeTab, setActiveTab] = useState('profile'); // 15 separate useEffects // 10 different event handlers // Multiple conditional renders // 300+ lines of chaos };
这种方法不仅令人烦恼,而且还会造成长期问题。想象一下需要重命名 user 属性。突然,您在五个或更多地方更新它。更糟糕的是,您最终将组件与它们甚至不使用的数据绑定在一起。
没有必要用你的道具来玩烫手山芋。这里有两个实用的解决方案,可以完全避免钻孔。
1。使用共享数据的上下文
如果跨应用程序的不同部分访问一段数据,React 的 Context API 可以简化事情。
// A cleaner, smarter approach const UserDashboard = () => { return ( <div> <UserProfile /> <OrderHistory /> <NotificationCenter /> <UserSettings /> </div> ); };
2。使用组合来提高灵活性
不要通过层强制道具,而是重组组件,以便它们只传递需要的内容。
// This is exhausting const App = () => { const [user, setUser] = useState({}); return ( <Layout user={user}> <Sidebar user={user}> <Navigation user={user}> <UserMenu user={user} /> </Navigation> </Sidebar> </Layout> ); };
要点
上下文非常适合应用程序范围的数据,例如用户信息、主题或全局设置。然而,它并不总是最好的选择——不要过度使用它。对于局部状态,考虑是否可以调整组件结构以避免完全钻取。
目标是让你的组件清晰且可维护。避免螺旋钻探将为您节省时间、减少挫败感并避免日后无数令人头疼的问题。
您可能听说过关于过早优化是万恶之源的名言。好吧,欢迎来到组件级邪恶。我说的是那些时候,我们甚至不知道是否需要两次之前就尝试让所有东西都可重复使用。
通常是这样的:
const UserContext = createContext(); const App = () => { const [user, setUser] = useState({}); return ( <UserContext.Provider value={user}> <Layout> <Sidebar> <Navigation /> </Sidebar> </Layout> </UserContext.Provider> ); }; // Use it only where needed const UserMenu = () => { const user = useContext(UserContext); return <div>{user.name}</div>; };
让你的组件自然发展。为您知道您今天需要的东西而构建。如果出现新的需求,请在可以清楚地证明其合理性的情况下添加功能。过早的优化会浪费时间,增加复杂性,而且很少有回报。
记住: YAGNI 原则(你不会需要它)也适用于组件。当你真正遇到了他们正在解决的问题时,最好的抽象就会出现。过度设计可能会让人感觉很主动,但简单总是胜出。
这是不良效果管理的经典示例。看起来很眼熟吗?
// Focused components for better clarity const Navigation = ({ children }) => { return <nav>{children}</nav>; }; // Pass data only where required const App = () => { const user = useUser(); return ( <Layout> <Navigation> <UserMenu user={user} /> </Navigation> </Layout> ); };
常见错误和修复
1) 混乱的数据获取
糟糕的数据处理产生的错误比它解决的错误还要多。这是一种更简洁的方法:
// Behold, the over-engineered button const Button = ({ children, variant = 'primary', size = 'medium', isFullWidth = false, isDisabled = false, isLoading = false, leftIcon, rightIcon, onClick, customClassName, style, loadingText = 'Loading...', tooltipText, animationType, // ... 10 more props }) => { // 50 lines of prop processing logic return ( <button className={generateComplexClassNames()} > <h3> Why This Hurts </h3> <ul> <li>Your “simple” button now requires an instruction manual.</li> <li>Most of those 15+ props will never be used.</li> <li>Making updates becomes risky because you have to account for endless combinations.</li> <li>Writing tests becomes painful, with a hundred possible scenarios to consider.</li> </ul> <h3> Better Approach: </h3> <p>Instead of building for every imaginable scenario, start small and let your components grow as needed.<br> </p> <pre class="brush:php;toolbar:false">// Start simple const Button = ({ children, onClick, variant = 'primary' }) => { return ( <button className={`btn btn-${variant}`} onClick={onClick} > {children} </button> ); } // Create specific buttons when you actually need them const LoadingButton = ({ isLoading, children, ...props }) => { return ( <Button {...props}> {isLoading ? 'Loading...' : children} </Button> ); }
2)忘记清理
总是清理干净自己:
const UserProfile = ({ userId }) => { const [user, setUser] = useState(null); const [posts, setPosts] = useState([]); // Dependency array woes useEffect(() => { fetchUserData(userId); fetchUserPosts(userId); // No cleanup? Yikes. }, []); // eslint-disable-line react-hooks/exhaustive-deps // Multiple effects, all tangled useEffect(() => { const subscription = subscribeToUserStatus(userId); }, [userId]); // Cleanup? What cleanup? useEffect(() => { const interval = setInterval(checkNotifications, 5000); }, []); };
3) 忽略竞争条件
使用此技术避免重叠请求:
// Improved version const UserProfile = ({ userId }) => { const { data: user, isLoading } = useQuery( ['user', userId], () => fetchUserData(userId) ); // Keep concerns separate const { data: posts } = useQuery( ['posts', userId], () => fetchUserPosts(userId), { enabled: !!user } ); };
快速提示
让我们来谈谈那些偷偷摸摸的性能问题。他们是那种在雷达下飞行的人,因为一切看起来都很好——直到事实并非如此。让我们揭开这些无声的罪魁祸首,看看如何解决它们。
问题
这是一个存在一些微妙性能缺陷的组件:
// Please, no more of this const UserDashboard = () => { const [userData, setUserData] = useState(null); const [orders, setOrders] = useState([]); const [notifications, setNotifications] = useState([]); const [settings, setSettings] = useState({}); const [isEditing, setIsEditing] = useState(false); const [activeTab, setActiveTab] = useState('profile'); // 15 separate useEffects // 10 different event handlers // Multiple conditional renders // 300+ lines of chaos };
你能发现问题吗?让我们把它们分解一下。
修复
1) 记住昂贵的计算
不要在每次渲染时重新计算所有内容,而是使用 useMemo 来缓存结果:
// A cleaner, smarter approach const UserDashboard = () => { return ( <div> <UserProfile /> <OrderHistory /> <NotificationCenter /> <UserSettings /> </div> ); };
这避免了在每次渲染时重新计算数据并重新创建事件处理程序。它还可以防止带有备忘录的子组件不必要的重新渲染。
2) 高效的状态更新
糟糕的状态管理也会降低性能。这是处理搜索结果等更新的更好方法:
// This is exhausting const App = () => { const [user, setUser] = useState({}); return ( <Layout user={user}> <Sidebar user={user}> <Navigation user={user}> <UserMenu user={user} /> </Navigation> </Sidebar> </Layout> ); };
去抖动确保我们不会在每次击键时获取数据,从而减少不必要的 API 调用和重新渲染。
快速性能提示
构建组件并不是什么复杂的事情,但说实话,我们很容易养成坏习惯。我犯过这些错误中的每一个(有时仍然会犯)。没关系。重要的是尽早发现它们、修复它们并避免粗糙的代码库。
更好组件的快速清单
✅ 单一职责:如果您无法用一句话概括组件的工作,那么就该将其分解。
✅ 道具管理:层层传递道具?考虑使用上下文或利用组合来代替。
✅ 状态和效果:集中效果,正确清理它们,并让现代工具处理复杂的数据获取。
✅ 性能:不要为了优化而优化——首先要衡量。在需要时巧妙地使用 memo、useMemo 和 useCallback 等工具。
✅ 从简单开始:解决你现在遇到的问题,而不是将来可能遇到的问题。
最好的组件不是华而不实或过于聪明 - 它们是您的团队可以在六个月内阅读和维护的组件。
记住:这些不是硬性规则,只是指导方针。有时你会打破它们,那很好。我们的目标不是完美,而是构建一些组件,让您在以后重新审视自己的职业选择时不会产生疑问。
以上是停止犯这些组件错误的详细内容。更多信息请关注PHP中文网其他相关文章!