2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaScriptにおける再利用可能なコンポーネント設計:引数・状態・副作用の分離戦略

Posted at

概要

「再利用できるコンポーネント」とは、使い回し可能なUI部品という意味だけではない。
それは**“責務が単一で、入力(Props)・状態(State)・副作用(Effects)が明確に分離された構造”**である。

JavaScript(およびReact/VueなどのUIライブラリ)において、コンポーネントが肥大化する原因は、
処理の責任と関心ごとが混在していることにある。

本稿では、引数・状態・副作用の構造的分離による、拡張可能でテスタブルな再利用コンポーネント設計戦略を解説する。


1. 責務の単一性(SRP)を軸に設計

// ❌ 責務が混在:描画・ロジック・副作用
function UserCard({ userId }) {
  const [user, setUser] = useState(null);
  useEffect(() => {
    fetch(`/api/user/${userId}`).then(res => res.json()).then(setUser);
  }, [userId]);
  return <div>{user?.name}</div>;
}
// ✅ 分離:データ取得と表示の責務を明確に
function useUser(userId) {
  const [user, setUser] = useState(null);
  useEffect(() => {
    fetch(`/api/user/${userId}`).then(res => res.json()).then(setUser);
  }, [userId]);
  return user;
}

function UserCard({ userId }) {
  const user = useUser(userId);
  return <div>{user?.name}</div>;
}
  • ✅ ロジックの分離により、テストや差し替えが可能
  • ✅ 表示コンポーネントは純粋関数的に記述できる

2. 入力(Props)設計の明確化

function Button({ label, onClick, type = 'primary' }) {
  return <button className={`btn btn-${type}`} onClick={onClick}>{label}</button>;
}
  • ✅ 引数は明示的に列挙し、オブジェクトごとに渡さない
  • label, onClick, type などの責務は明確で単純

3. 状態(State)の封じ込め戦略

  • ✅ 「内部状態」と「外部から渡す状態」は明確に分離する
  • ✅ 入力が全てで決まるものは stateless(純粋)コンポーネント
function Counter({ count, onIncrement }) {
  return (
    <button onClick={onIncrement}>
      Count: {count}
    </button>
  );
}
function CounterContainer() {
  const [count, setCount] = useState(0);
  return <Counter count={count} onIncrement={() => setCount(count + 1)} />;
}
  • ✅ 再利用性を上げるには、stateful → statelessへの分離が基本戦略

4. 副作用の構造化と限定化

useEffect(() => {
  document.title = `Page ${page}`;
}, [page]);
  • ✅ 副作用は useEffect や外部API呼び出しとして明示的に分離
  • 表示・ロジック・副作用を物理的にファイル分離することも検討

設計判断フロー

① このコンポーネントは表示だけか? → statelessに切り出し可

② 副作用はどこで起きているか? → useEffect に限定し、他から切り離す

③ 状態は本当に必要か? → propsで注入可能なものは外部化

④ テスト可能か? → 表示とロジックを分離して、Mockが差し替えられる構造か

⑤ コンポーネントの役割は1文で説明できるか? → 単一責務に絞る

よくあるミスと対策

❌ 状態を持つコンポーネントが表示とAPI処理とイベント処理すべて抱える

→ ✅ 状態・副作用・ロジックをフック化 or containerに分離


❌ 小さすぎるコンポーネント分割でprops地獄に

→ ✅ 責務と再利用性を基準に分割深度を調整


❌ propsとしてオブジェクトや関数をそのまま渡して再レンダリング地獄

→ ✅ useMemo / useCallback / 分解して渡すことで安定性確保


結語

再利用可能なコンポーネント設計とは、「どこでも使える汎用UI部品」ではない。
それは**“引数・状態・副作用の境界を設計として分離し、拡張性と保守性を担保する構造戦略”**である。

  • 表示は表示、ロジックはロジック、副作用は副作用で切り分ける
  • 再利用性とは「どこでも使える」ではなく、「責務が明確で読みやすい」こと
  • 入力と出力を制御可能な設計が、保守性・テスタビリティを大きく高める

JavaScriptにおける再利用可能なコンポーネント設計とは、
“責務の分離を通じて、再利用・差し替え・検証可能性を最大化するための構造設計である。”

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?