コンポーネントの間違いをやめてください

DDD
リリース: 2024-11-19 10:43:03
オリジナル
898 人が閲覧しました

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 の依存関係は、毎週の買い物リストのようになります。
  • 機能のクリープ拒否: 機能がもう 1 つ増えても問題ない、と自分に言い聞かせます。

ブレイク・イット・ダウン

解決策は?すべてを単一のコンポーネントにする代わりに、責任をより小さく集中した部分に分割します。

// A cleaner, smarter approach
const UserDashboard = () => {
  return (
    <div>
      <UserProfile />
      <OrderHistory />
      <NotificationCenter />
      <UserSettings />
    </div>
  );
};
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

重要な原則: ロジック >レイアウト

リファクタリングするときは、画面上の見た目に基づいてコンポーネントを分割しないでください。責任ごとに分けてください。この機能は独自のコンポーネントに値するか? 自問してください。ユーザー プロフィールや注文履歴など、何か別のものを処理している場合は、おそらく処理されます。

ヒント: 優れたコンポーネントは、1 つのことを適切に実行します。その目的を一文で説明するのが難しい場合は、やりすぎている可能性があります。

プロップドリル地獄

「小道具を渡す」というあまり面白くないゲームについて話しましょう。深くネストされた子に到達するためだけに同じプロップを複数のコンポーネントに渡したことがあれば、プロップドリル地獄にはまっていることになります。

// 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
};
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

このアプローチは面倒なだけでなく、長期的な問題を引き起こします。ユーザー プロパティの名前を変更する必要があると想像してください。突然、5 か所以上で更新するようになりました。さらに悪いことに、コンポーネントを使用すらしていないデータに結び付けることになります。

これを修正する方法

小道具を使ってホットポテトをする必要はありません。ここでは、穴あけを完全に回避するための 2 つの実用的な解決策を紹介します。

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 原則 (You Aren’t Gonna Need It) はコンポーネントにも適用されます。最高の抽象化は、解決しようとしている問題に実際に遭遇したときに生まれます。過剰なエンジニアリングは積極的に行われるように感じるかもしれませんが、常にシンプルさが勝利します。

副作用の不始末

これは悪影響管理の典型的な例です。

// 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 を使用する前に考えてください: 場合によっては、useEffect がまったく必要ない場合もあります。
  • 焦点を絞ってください: 1 つのエフェクトで 1 つの責任を処理する必要があります。
  • 常にクリーンアップ: サブスクリプション、間隔、イベント リスナーには注意が必要です。
  • 適切なツールを使用します。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 呼び出しや再レンダリングが削減されます。

パフォーマンスに関する簡単なヒント

  • メモ化を多用しないでください。最適化する価値がある場合にのみ最適化してください。
  • インライン関数を避ける: 安定した参照によりパフォーマンスが向上します。
  • プロップを予測可能に保つ: 浅くて安定したプロップは、コンポーネントの効率を維持するのに役立ちます。
  • 大きなリストを分割する:react-window のようなツールは、大きなデータセットを適切に処理できます。
  • 状態を近づける: 実際に必要な状態のみを管理します。
  • DevTools を使用したプロファイル: 最適化する前に必ず測定します。

結論

コンポーネントの構築はロケット科学ではありませんが、正直に言いましょう。悪い習慣につまずいてしまうのは簡単です。私はこれらすべての間違いを犯してきました(そして今でも時々犯します)。大丈夫です。重要なのは、それらを早期に発見し、修正し、粗雑なコードベースを避けることです。

より良いコンポーネントのためのクイックチェックリスト

✅ 単一の責任: コンポーネントのジョブを一文で要約できない場合は、コンポーネントを分解してください。

✅ 小道具の管理: 小道具をレイヤーの上にレイヤーを介して渡しますか?代わりにコンテキストを使用するか、合成を活用することを検討してください。

✅ 状態とエフェクト: エフェクトに焦点を当て、適切にクリーンアップし、最新のツールで複雑なデータの取得を処理させます。

✅ パフォーマンス: 目的のために最適化するのではなく、まず測定してください。必要に応じて、memo、useMemo、useCallback などのツールを賢く使用してください。

✅ シンプルに始める: いつか起こるかもしれない問題ではなく、今抱えている問題を解決してください。

最高のコンポーネントとは、派手でも賢すぎるものでもありません。チームが 6 か月後にうめき声を上げることなく読み、保守できるコンポーネントです。

覚えておいてください: これらは難しいルールではなく、単なるガイドラインです。時々壊れてしまうこともありますが、それはまったく問題ありません。目標は完璧ではありません。後でキャリアの選択を見直したときに、自分のキャリアの選択に疑問を抱かないようにするためのコンポーネントを構築することです。

以上がコンポーネントの間違いをやめてくださいの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート