グローバル依存を隠蔽していないか?Contextを使うHookのレビュー

グローバルステートを共有する目的で導入されるContext構造は、Reactにおける状態管理において有用な仕組みのひとつです。しかし、useXxxContext のようなカスタムHookを通じてContext値を参照する構造は、一見すると抽象化されているようで、実際には依存元が隠蔽されているだけである場合があります。

レビューアーとしては、こうした構造に潜むグローバル依存の不透明化を見逃さず、「構造としての明快さ」よりも「依存性の可視性」を優先すべき場面を識別する必要があります。

Context構造による依存関係の不明瞭化

useUserContext.ts
const UserContext = createContext<User | null>(null);

export const useUserContext = () => {
  const context = useContext(UserContext);
  if (!context) throw new Error('No UserContext');
  return context;
};
任意のコンポーネント
export const UserProfile = () => {
  const user = useUserContext();

  return <p>{user.name}</p>;
};

このような構造はReactコードベースでは一般的ですが、以下の観点に注意が必要です。

  • useUserContextという命名により、「グローバル依存であること」がコード上で直感的に伝わらない
  • importパスや使用箇所から依存元が明確にならず、ローカル変数のように錯覚される
  • Propsによる依存注入と異なり、「どこで値が供給されているか」の追跡コストが高い
グローバル依存のレビュー例
@Reviewer: useUserContextが暗黙的にグローバルな状態を参照しており、依存元(Provider)が不明瞭になっています。Propsによる明示的な依存伝播が適切でないか、責務単位で再検討してください。
Contextとは

React Contextは、ツリー構造の深い階層に渡って値を共有するための仕組みで、主にグローバルステートのような横断的な依存を扱う際に用いられる。Providerで囲まれた範囲内でuseContextにより値を取得できる。

Context Hookの抽象化と責務の見極め

以下のように、Contextを内包するHookが別のHookやロジックと結合している構造は、責務の追跡をさらに困難にします。

useAdminFeatures.ts
export const useAdminFeatures = () => {
  const user = useUserContext();

  const canEdit = user.role === 'admin';
  const canDelete = user.permissions.includes('delete');

  return { canEdit, canDelete };
};

このようなHookを使用すると、実際には「グローバルステート(Context)」に依存しているにも関わらず、呼び出し側からは単なるユーティリティのように見えてしまいます。

Comment
@Reviewer: useAdminFeaturesの実態はグローバル状態への依存に基づいており、利用者側ではその構造が把握しにくい点に注意してください。外部に依存するロジックであることが明示されるよう、命名・構造に工夫が必要です。

Context Hookの依存構造

UML Diagram

レビューで問うべき判断基準

  • Contextに依存したHookが再利用可能か、または特定画面専用か
  • そのHookの責務に対してContext依存が本当に必要か
  • Context依存がPropsによる伝播で代替可能でないか
  • 呼び出し元のコードから依存構造が追いやすくなっているか

グローバルな依存を伴うHook構造では、ロジックの再利用性・拡張性よりも、依存関係の明示性と構造の可視性が優先されるべきです。