首页  >  文章  >  web前端  >  停止犯这些组件错误

停止犯这些组件错误

DDD
DDD原创
2024-11-19 10:43:03891浏览

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