首页 web前端 js教程 停止犯这些组件错误

停止犯这些组件错误

Nov 19, 2024 am 10:43 AM

Stop Making These Component Mistakes

事实是,组件看似简单。上手很容易——定义一个函数,返回一些 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
};
登录后复制
登录后复制
登录后复制

听起来很熟悉吗?

如何判断你是否有罪

如果出现以下情况,您的组件可能已变成“一切组件”:

  • 状态过载:您正在跟踪超过 3-4 个独立的状态。
  • 无休止的滚动:你花了太多时间寻找特定的功能或逻辑。
  • 依赖项膨胀: 你的 useEffect 依赖项看起来就像你的每周购物清单。
  • 否认功能蔓延:你不断告诉自己,多一个功能不会有什么坏处。

分解它

解决方案?不要使用单一的所有组件,而是将职责分解为更小的、更有针对性的部分。

// 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 }  
  );  
};
登录后复制

快速提示

  • 使用 useEffect 之前请三思:有时候,你可能根本不需要它。
  • 保持专注:一种效果应该承担一种责任。
  • 始终清理:订阅、间隔和事件监听器需要关注。
  • 使用正确的工具:像 React Query 这样的库可以简化数据获取和缓存。
  • 不要用 eslint-disable 作弊:修复依赖问题而不是隐藏它们。

性能盲点

让我们来谈谈那些偷偷摸摸的性能问题。他们是那种在雷达下飞行的人,因为一切看起来都很好——直到事实并非如此。让我们揭开这些无声的罪魁祸首,看看如何解决它们。

问题

这是一个存在一些微妙性能缺陷的组件:

// 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 调用和重新渲染。

快速性能提示

  • 不要过度使用记忆:仅在值得的时候进行优化。
  • 避免内联函数:稳定的引用可以提高性能。
  • 保持 props 可预测:浅且稳定的 props 有助于组件保持高效。
  • 分解大型列表:像react-window这样的工具可以优雅地处理大数据集。
  • 将状态移得更近:仅在实际需要的地方管理状态。
  • 使用 DevTools 进行分析:优化前始终进行测量。

结论

构建组件并不是什么复杂的事情,但说实话,我们很容易养成坏习惯。我犯过这些错误中的每一个(有时仍然会犯)。没关系。重要的是尽早发现它们、修复它们并避免粗糙的代码库。

更好组件的快速清单

✅ 单一职责:如果您无法用一句话概括组件的工作,那么就该将其分解。

✅ 道具管理:层层传递道具?考虑使用上下文或利用组合来代替。

✅ 状态和效果:集中效果,正确清理它们,并让现代工具处理复杂的数据获取。

✅ 性能:不要为了优化而优化——首先要衡量。在需要时巧妙地使用 memo、useMemo 和 useCallback 等工具。

✅ 从简单开始:解决你现在遇到的问题,而不是将来可能遇到的问题。

最好的组件不是华而不实或过于聪明 - 它们是您的团队可以在六个月内阅读和维护的组件。

记住:这些不是硬性规则,只是指导方针。有时你会打破它们,那很好。我们的目标不是完美,而是构建一些组件,让您在以后重新审视自己的职业选择时不会产生疑问。

以上是停止犯这些组件错误的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

<🎜>:泡泡胶模拟器无穷大 - 如何获取和使用皇家钥匙
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系统,解释
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆树的耳语 - 如何解锁抓钩
3 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

热门话题

Java教程
1672
14
CakePHP 教程
1428
52
Laravel 教程
1332
25
PHP教程
1277
29
C# 教程
1257
24
Python vs. JavaScript:学习曲线和易用性 Python vs. JavaScript:学习曲线和易用性 Apr 16, 2025 am 12:12 AM

Python更适合初学者,学习曲线平缓,语法简洁;JavaScript适合前端开发,学习曲线较陡,语法灵活。1.Python语法直观,适用于数据科学和后端开发。2.JavaScript灵活,广泛用于前端和服务器端编程。

JavaScript和Web:核心功能和用例 JavaScript和Web:核心功能和用例 Apr 18, 2025 am 12:19 AM

JavaScript在Web开发中的主要用途包括客户端交互、表单验证和异步通信。1)通过DOM操作实现动态内容更新和用户交互;2)在用户提交数据前进行客户端验证,提高用户体验;3)通过AJAX技术实现与服务器的无刷新通信。

JavaScript在行动中:现实世界中的示例和项目 JavaScript在行动中:现实世界中的示例和项目 Apr 19, 2025 am 12:13 AM

JavaScript在现实世界中的应用包括前端和后端开发。1)通过构建TODO列表应用展示前端应用,涉及DOM操作和事件处理。2)通过Node.js和Express构建RESTfulAPI展示后端应用。

了解JavaScript引擎:实施详细信息 了解JavaScript引擎:实施详细信息 Apr 17, 2025 am 12:05 AM

理解JavaScript引擎内部工作原理对开发者重要,因为它能帮助编写更高效的代码并理解性能瓶颈和优化策略。1)引擎的工作流程包括解析、编译和执行三个阶段;2)执行过程中,引擎会进行动态优化,如内联缓存和隐藏类;3)最佳实践包括避免全局变量、优化循环、使用const和let,以及避免过度使用闭包。

Python vs. JavaScript:社区,图书馆和资源 Python vs. JavaScript:社区,图书馆和资源 Apr 15, 2025 am 12:16 AM

Python和JavaScript在社区、库和资源方面的对比各有优劣。1)Python社区友好,适合初学者,但前端开发资源不如JavaScript丰富。2)Python在数据科学和机器学习库方面强大,JavaScript则在前端开发库和框架上更胜一筹。3)两者的学习资源都丰富,但Python适合从官方文档开始,JavaScript则以MDNWebDocs为佳。选择应基于项目需求和个人兴趣。

Python vs. JavaScript:开发环境和工具 Python vs. JavaScript:开发环境和工具 Apr 26, 2025 am 12:09 AM

Python和JavaScript在开发环境上的选择都很重要。1)Python的开发环境包括PyCharm、JupyterNotebook和Anaconda,适合数据科学和快速原型开发。2)JavaScript的开发环境包括Node.js、VSCode和Webpack,适用于前端和后端开发。根据项目需求选择合适的工具可以提高开发效率和项目成功率。

C/C在JavaScript口译员和编译器中的作用 C/C在JavaScript口译员和编译器中的作用 Apr 20, 2025 am 12:01 AM

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。 1)C 用于解析JavaScript源码并生成抽象语法树。 2)C 负责生成和执行字节码。 3)C 实现JIT编译器,在运行时优化和编译热点代码,显着提高JavaScript的执行效率。

从网站到应用程序:JavaScript的不同应用 从网站到应用程序:JavaScript的不同应用 Apr 22, 2025 am 12:02 AM

JavaScript在网站、移动应用、桌面应用和服务器端编程中均有广泛应用。1)在网站开发中,JavaScript与HTML、CSS一起操作DOM,实现动态效果,并支持如jQuery、React等框架。2)通过ReactNative和Ionic,JavaScript用于开发跨平台移动应用。3)Electron框架使JavaScript能构建桌面应用。4)Node.js让JavaScript在服务器端运行,支持高并发请求。

See all articles