Agent Skills by ALSEL
汎用ソフトウェア開発⭐ リポ 198品質スコア 77/100

hooks-pattern

Reactコンポーネント間でステートフルなロジックを再利用するためのReact Hooksについて説明します。フォーム処理、購読、サイドエフェクトなどの共通の動作をカスタムフックに抽出して再利用可能にしたい場合に活用できます。

description の原文を見る

Teaches React Hooks for reusing stateful logic across components. Use when extracting shared behavior like form handling, subscriptions, or side effects into reusable custom hooks.

SKILL.md 本文

Hooksパターン

目次

React 16.8ではHooksという新しい機能が導入されました。Hooksを使用すると、ES2015クラスコンポーネントを使用することなく、Reactのステートとライフサイクルメソッドを使用できます。

Hooksは必ずしもデザインパターンではありませんが、アプリケーションのデザインにおいて非常に重要な役割を果たします。従来の多くのデザインパターンはHooksで置き換えることができます。

使用するべき場面

  • 関数コンポーネントにステートまたはライフサイクルの振る舞いを追加する必要があるときに使用してください
  • 複数のコンポーネント間でステートフルなロジックを抽出して再利用するときに役立ちます
  • より清潔で構成可能なコードのためにクラスコンポーネントの代わりにこれを使用してください

使用方法

  • useStateを関数コンポーネントのローカルステートに、useEffectをサイドエフェクトに使用します
  • カスタムHooks(useで始まるプレフィックス)を作成して、再利用可能なロジックをカプセル化して共有します
  • Hooksのルールに従います:トップレベルでのみHooksを呼び出し、React関数内でのみ呼び出します
  • 不要なuseEffectは避けてください。派生ステートはコンポーネント本体で直接計算してください
  • 可能な限り、手動のuseMemo/useCallbackの代わりにReact Compilerにメモ化を任せてください

詳細

クラスコンポーネント

Hooksが導入される前のReactでは、コンポーネントにステートとライフサイクルメソッドを追加するためにクラスコンポーネントを使用する必要がありました。Reactの典型的なクラスコンポーネントは次のようになります:

class MyComponent extends React.Component {
  /* Adding state and binding custom methods */
  constructor() {
    super()
    this.state = { ... }

    this.customMethodOne = this.customMethodOne.bind(this)
    this.customMethodTwo = this.customMethodTwo.bind(this)
  }

  /* Lifecycle Methods */
  componentDidMount() { ...}
  componentWillUnmount() { ... }

  /* Custom methods */
  customMethodOne() { ... }
  customMethodTwo() { ... }

  render() { return { ... }}
}

クラスコンポーネントは、コンストラクタでステートを含むことができ、componentDidMountcomponentWillUnmountなどのライフサイクルメソッドを使用してコンポーネントのライフサイクルに基づくサイドエフェクトを実行でき、クラスに追加のロジックを追加するカスタムメソッドを含むことができます。

React Hooksの導入後もクラスコンポーネントを使用できますが、クラスコンポーネントの使用にはいくつかの欠点があります。クラスコンポーネント使用時の最も一般的な問題をいくつか見てみましょう。

ES2015クラスの理解

React Hooksが導入される前は、ステートとライフサイクルメソッドを処理できるのはクラスコンポーネントだけだったため、追加の機能を追加するために関数コンポーネントをクラスコンポーネントにリファクタリングしなければならないことがよくありました。

この例では、ボタンとして機能する単純なdivがあります。

function Button() {
  return <div className="btn">disabled</div>;
}

常にdisabledを表示する代わりに、ユーザーがボタンをクリックしたときにenabledに変更し、そのときにボタンに追加のCSSスタイルを追加したいとします。

そのためには、ステータスがenableddisabledかを知るためにコンポーネントにステートを追加する必要があります。つまり、関数コンポーネント全体をリファクタリングし、ボタンのステートを追跡するクラスコンポーネントにする必要があります。

export default class Button extends React.Component {
  constructor() {
    super();
    this.state = { enabled: false };
  }

  render() {
    const { enabled } = this.state;
    const btnText = enabled ? "enabled" : "disabled";

    return (
      <div
        className={`btn enabled-${enabled}`}
        onClick={() => this.setState({ enabled: !enabled })}
      >
        {btnText}
      </div>
    );
  }
}

この例では、コンポーネントは非常に小さく、リファクタリングはそれほど大した問題ではありません。しかし、実際のコンポーネントはおそらく数多くのコード行を含んでいるため、コンポーネントのリファクタリングがはるかに困難になります。

コンポーネントのリファクタリング中に誤って動作を変更しないようにする必要がある他に、ES2015クラスの仕組みを理解する必要もあります。カスタムメソッドをなぜbindする必要があるのですか?constructorは何をするのですか?thisキーワードはどこから来ているのですか?データフローを誤って変更することなくコンポーネントを正しくリファクタリングする方法を知ることは難しい場合があります。

再構造化

複数のコンポーネント間でコードを共有する一般的な方法は、Higher Order ComponentまたはRender Propsパターンを使用することです。両方のパターンは有効で良い実践ですが、後の段階でこれらのパターンを追加するには、アプリケーションを再構造化する必要があります。

アプリを再構造化するだけでなく、コンポーネントが大きいほど難しくなり、より深くネストされたコンポーネント間でコードを共有するために多くのラッパーコンポーネントを持つことは、ラッパーヘルと呼ばれるものにつながる可能性があります。デベロッパーツールを開いて、以下のような構造を見ることは珍しくありません:

<WrapperOne>
  <WrapperTwo>
    <WrapperThree>
      <WrapperFour>
        <WrapperFive>
          <Component>
            <h1>Finally in the component!</h1>
          </Component>
        </WrapperFive>
      </WrapperFour>
    </WrapperThree>
  </WrapperTwo>
</WrapperOne>

ラッパーヘルは、アプリケーションを通じたデータフローの理解を困難にする可能性があり、予期しない動作が発生している理由を把握することがより難しくなる可能性があります。

複雑性

クラスコンポーネントに更多くのロジックを追加すると、コンポーネントのサイズが急速に増加します。そのコンポーネント内のロジックは絡み合い、構造化されていない状態になる可能性があり、開発者がクラスコンポーネント内で特定のロジックがどこで使用されているかを理解することが困難になる可能性があります。これにより、デバッグとパフォーマンス最適化がより困難になります。

ライフサイクルメソッドはコード内でかなりの重複が必要です。

コンポーネントが小さい場合でも、コンポーネント内のロジックはすでに複雑になる可能性があります。特定の部分はcounterロジック用であり、他の部分はwidthロジック用です。コンポーネントが成長するにつれて、コンポーネント内でロジックを構造化したり、コンポーネント内で関連するロジックを見つけたりすることがますます難しくなります。

絡み合ったロジックに加えて、ライフサイクルメソッド内でもロジックを重複させています。componentDidMountcomponentWillUnmountの両方で、ウィンドウのresizeイベントに基づいてアプリの動作をカスタマイズしています。

Hooks

クラスコンポーネントがReactの常に素晴らしい機能ではないことは明らかです。React開発者がクラスコンポーネント使用時に遭遇する可能性のある一般的な問題を解決するために、ReactはReact Hooksを導入しました。React Hooksはコンポーネントのステートとライフサイクルメソッドを管理するために使用できる関数です。React Hooksは以下を可能にします:

  • 関数コンポーネントにステートを追加する
  • componentDidMountcomponentWillUnmountなどのライフサイクルメソッドを使用することなくコンポーネントのライフサイクルを管理する
  • アプリ全体の複数のコンポーネント間で同じステートフルなロジックを再利用する

まず、React Hooksを使用して関数コンポーネントにステートを追加する方法を見てみましょう。

State Hook

Reactは関数コンポーネント内でステートを管理するHookを提供しており、useStateと呼ばれています。

useState hookを使用して、クラスコンポーネントを関数コンポーネントにリファクタリングする方法を見てみましょう。Inputという名前のクラスコンポーネントがあり、単にinput フィールドをレンダリングします。ユーザーがinput フィールドに何か入力するたびに、ステート内のinputの値が更新されます。

class Input extends React.Component {
  constructor() {
    super();
    this.state = { input: "" };

    this.handleInput = this.handleInput.bind(this);
  }

  handleInput(e) {
    this.setState({ input: e.target.value });
  }

  render() {
    <input onChange={handleInput} value={this.state.input} />;
  }
}

useState hookを使用するために、Reactが提供するuseStateメソッドにアクセスする必要があります。useStateメソッドは引数を期待しています:これはステートの初期値で、この場合は空の文字列です。

useStateメソッドから2つの値をデストラクチャできます:

  1. ステートの現在の値
  2. ステートを更新するメソッド
const [value, setValue] = React.useState(initialValue);

最初の値はクラスコンポーネントのthis.state.[value]と比較できます。 2番目の値はクラスコンポーネントのthis.setStateメソッドと比較できます。

input の値を扱っているため、ステートの現在の値をinputと呼び、ステートを更新するメソッドをsetInputと呼びましょう。初期値は空の文字列である必要があります。

const [input, setInput] = React.useState("");

これで、Inputクラスコンポーネントをステートフルな関数コンポーネントにリファクタリングできます。

function Input() {
  const [input, setInput] = React.useState("");

  return <input onChange={(e) => setInput(e.target.value)} value={input} />;
}

inputフィールドの値はステートフルなinputステートの現在の値と同じです(クラスコンポーネントの例のように)。ユーザーがinput フィールドに入力すると、setInputメソッドを使用してinputステートの値が適切に更新されます。

Effect Hook

useStateコンポーネントを使用して関数コンポーネント内でステートを処理できることを見ましたが、クラスコンポーネントのもう1つの利点はコンポーネントにライフサイクルメソッドを追加する可能性でした。

useEffect hookを使用して、コンポーネントのライフサイクルに**「フック」する**ことができます。useEffect hookは、componentDidMountcomponentDidUpdate、およびcomponentWillUnmountライフサイクルメソッドを効果的に結合します。

componentDidMount() { ... }
useEffect(() => { ... }, [])

componentWillUnmount() { ... }
useEffect(() => { return () => { ... } }, [])

componentDidUpdate() { ... }
useEffect(() => { ... })

State Hook のセクションで使用したinput の例を使用しましょう。ユーザーがinput フィールドに何か入力するたびに、その値もコンソールにログしたいとします。

inputの値を「リッスン」するuseEffect hookを使用する必要があります。そのためには、useEffect hookの依存配列inputを追加します。依存配列はuseEffect hookが受け取る2番目の引数です。

useEffect(() => {
  console.log(`The user typed ${input}`);
}, [input]);

ユーザーが値を入力するたびに、input の値がコンソールにログされるようになります。

カスタムHooks

Reactが提供する組み込みHooks(useStateuseEffectuseReduceruseRefuseContextuseMemouseImperativeHandleuseLayoutEffectuseDebugValueuseCallback)の他に、独自のカスタムHooksを簡単に作成できます。

すべてのHooksがuseで始まることに気づいたかもしれません。HooksがHooksのルールに違反していないか確認するためにHooksをuseで開始することが重要です。

ユーザーがinput を書いているときに押下する可能性のある特定のキーを追跡したいとしましょう。カスタムHookは、ターゲットキーを引数として受け取ることができるはずです。

function useKeyPress(targetKey) {
  const [keyPressed, setKeyPressed] = React.useState(false);

  function handleDown({ key }) {
    if (key === targetKey) {
      setKeyPressed(true);
    }
  }

  function handleUp({ key }) {
    if (key === targetKey) {
      setKeyPressed(false);
    }
  }

  React.useEffect(() => {
    window.addEventListener("keydown", handleDown);
    window.addEventListener("keyup", handleUp);

    return () => {
      window.removeEventListener("keydown", handleDown);
      window.removeEventListener("keyup", handleUp);
    };
  }, []);

  return keyPressed;
}

キープレスのロジックをInputコンポーネントにローカルに保つ代わりに、同じロジックを何度も書き直すことなく、複数のコンポーネント全体でuseKeyPress hookを再利用できるようになります。

Hooksのもう1つの素晴らしい利点は、コミュニティがHooksを構築して共有できることです。以下は、コミュニティによって構築されたすべてのHooksを一覧にしており、アプリケーションで使用できるウェブサイトです:

React Hooksを使用してカウンターと幅の例を書き直すことで、App関数のロジックをいくつかの部分に分割できます:

  • useCountercountの現在の値、incrementメソッド、およびdecrementメソッドを返すカスタムHook。
  • useWindowWidth:ウィンドウの現在の幅を返すカスタムHook。
  • AppCounterWidthコンポーネントを返すステートフルな関数コンポーネント。

クラスコンポーネントの代わりにReact Hooksを使用することで、ロジックをより小さく再利用可能な部分に分割し、ロジックを分離することができました。

React Hooksを使用すると、コンポーネントのロジックをいくつかのより小さな部分に分割することが非常に明確になりました。同じステートフルなロジックを再利用することがはるかに簡単になり、コンポーネントをステートフルにしたい場合に、関数コンポーネントをクラスコンポーネントに書き直す必要がなくなりました。ES2015クラスの優れた知識はもはや必要ではなく、再利用可能なステートフルなロジックを持つことでコンポーネントのテスト性、柔軟性、可読性が向上します。

追加のHooksガイダンス

他のコンポーネントと同様に、記述したコードにHooksを追加したいときに使用される特別な関数があります。一般的なHook関数の簡単な概要を次に示します:

useState

useState Hookは、開発者がクラスコンポーネントに変換することなく、関数コンポーネント内でステートを更新および操作できるようにします。このHookの利点の1つは、シンプルで他のReact Hooksほど多くの複雑さが必要ないということです。

useEffect

useEffect Hookは、関数コンポーネントの主要なライフサイクルイベント中にコードを実行するために使用されます。関数コンポーネントの本体は、変異、サブスクリプション、タイマー、ロギング、およびその他のサイドエフェクトを許可しません。許可すると、UI内で混乱するバグと不整合につながる可能性があります。useEffect hookはこれらすべての「サイドエフェクト」を防止し、UIがスムーズに実行できるようにします。これはcomponentDidMountcomponentDidUpdate、およびcomponentWillUnmountの組み合わせで、すべて1つの場所にあります。

useContext

useContext Hookはコンテキストオブジェクト(React.createContextから返される値)を受け取り、そのコンテキストの現在のコンテキスト値を返します。useContext Hookはまた、React Context APIと連動して、様々なレベルを通じてアプリのプロップを渡す必要なしにアプリ全体でデータを共有します。

useContext hookに渡された引数はコンテキストオブジェクト自体でなければならず、useContextを呼び出すコンポーネントはコンテキスト値が変わるたびに常に再レンダリングされることに注意してください。

useReducer

useReducer HookはsetStateに代わるものを提供し、特に複数のサブ値を含む複雑なステートロジックがある場合、または次のステートが前のステートに依存している場合に特に好まれます。reducer関数と初期ステート入力を受け取り、配列デストラクチャリングによって現在のステートとdispatch関数を出力として返します。useReducerはまた、深い更新をトリガーするコンポーネントのパフォーマンスを最適化します。

利点

行数が少ない

Hooksは、ライフサイクルごとではなく、関心と機能によってコードをグループ化できます。これにより、コードが清潔で簡潔なだけでなく、より短くなります。以下は、Reactを使用した検索可能な製品データテーブルの単純なステートフルコンポーネントの比較で、useStateキーワードを使用した後のHooksでどのように見えるかです。

ステートフルコンポーネント

class TweetSearchResults extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      filterText: "",
      inThisLocation: false,
    };

    this.handleFilterTextChange = this.handleFilterTextChange.bind(this);
    this.handleInThisLocationChange =
      this.handleInThisLocationChange.bind(this);
  }

  handleFilterTextChange(filterText) {
    this.setState({
      filterText: filterText,
    });
  }

  handleInThisLocationChange(inThisLocation) {
    this.setState({
      inThisLocation: inThisLocation,
    });
  }

  render() {
    return (
      <div>
        <SearchBar
          filterText={this.state.filterText}
          inThisLocation={this.state.inThisLocation}
          onFilterTextChange={this.handleFilterTextChange}
          onInThisLocationChange={this.handleInThisLocationChange}
        />
        <TweetList
          tweets={this.props.tweets}
          filterText={this.state.filterText}
          inThisLocation={this.state.inThisLocation}
        />
      </div>
    );
  }
}

Hooksを使用した同じコンポーネント

const TweetSearchResults = ({ tweets }) => {
  const [filterText, setFilterText] = useState("");
  const [inThisLocation, setInThisLocation] = useState(false);
  return (
    <div>
      <SearchBar
        filterText={filterText}
        inThisLocation={inThisLocation}
        setFilterText={setFilterText}
        setInThisLocation={setInThisLocation}
      />
      <TweetList
        tweets={tweets}
        filterText={filterText}
        inThisLocation={inThisLocation}
      />
    </div>
  );
};

複雑なコンポーネントを単純化する

JavaScriptクラスは管理が難しく、ホットリロードでの使用が難しく、最適化が不十分な場合があります。React Hooksはこれらの問題を解決し、関数型プログラミングを簡単にします。Hooksの実装により、クラスコンポーネントは必要ありません。

ステートフルなロジックの再利用

JavaScriptのクラスは複数レベルの継承を促進し、全体的な複雑さとエラーの可能性が急速に増加します。しかし、Hooksを使用すると、クラスを書くことなくステートおよび他のReact機能を使用できます。Reactを使用すると、コードを何度も書き直す必要なく、常にステートフルなロジックを再利用できます。これにより、エラーの可能性が減少し、プレーンな関数での構成が可能になります。

非ビジュアルロジックを共有する

Hooksの実装まで、Reactは非ビジュアルロジックを抽出して共有する方法がありませんでした。これは最終的にHOCパターンとRender propsなどのより複雑なパターンをもたらし、一般的な問題を解決するだけです。しかし、Hooksの導入により、ステートフルロジックをシンプルなJavaScript関数に抽出できるようになったため、この問題は解決されました。

もちろん、Hooksの潜在的な欠点もいくつかあります:

  • ルールに従う必要があり、linterプラグインがなければ、どのルールが破られたかを知ることは難しいです。
  • 正しく使用するために、相当な時間を練習する必要があります(例:useEffect)。
  • 間違った使用に注意してください(例:useCallback、useMemo)。

React Hooks vs クラス

Hooksが Reactに導入されたとき、新しい問題が生じました:関数コンポーネントをHooksで使用する場合と、クラスコンポーネントを使用する場合をどうやって判断するのか?Hooksの助けを借りて、関数コンポーネントでもステートと部分的なライフサイクルHooksを取得できます。Hooksを使用すると、クラスを記述することなくローカルステートおよび他のReact機能を使用できます。

以下は、Hooksとクラスの間のいくつかの違いで、どちらを使用するかを決定するのに役立ちます:

React Hooksクラス
複数の階層を避け、コードをより明確にするのに役立ちます通常、HOCまたはrenderPropsを使用する場合、DevToolsで表示しようとするときに複数の階層でアプリを再構造化する必要があります
React コンポーネント間で統一性を提供します。クラスはバインドの必要性と関数が呼び出されるコンテキストを理解する必要があるため、人間とマシンの両方を混乱させます。

出典

ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ

詳細情報

作者
PatternsDev
リポジトリ
PatternsDev/skills
ライセンス
MIT
最終更新
2026/4/15

Source: https://github.com/PatternsDev/skills / ライセンス: MIT

関連スキル

汎用ソフトウェア開発⭐ リポ 39,967

doubt-driven-development

重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。

by addyosmani
汎用ソフトウェア開発⭐ リポ 1,175

apprun-skills

TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。

by yysun
OpenAIソフトウェア開発⭐ リポ 797

desloppify

コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。

by Git-on-my-level
汎用ソフトウェア開発⭐ リポ 39,967

debugging-and-error-recovery

テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。

by addyosmani
汎用ソフトウェア開発⭐ リポ 39,967

test-driven-development

テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。

by addyosmani
汎用ソフトウェア開発⭐ リポ 39,967

incremental-implementation

変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。

by addyosmani
本サイトは GitHub 上で公開されているオープンソースの SKILL.md ファイルをクロール・インデックス化したものです。 各スキルの著作権は原作者に帰属します。掲載に問題がある場合は info@alsel.co.jp または /takedown フォームよりご連絡ください。
原作者: PatternsDev · PatternsDev/skills · ライセンス: MIT