非同期処理を抱える構造のレビュー指針
はじめに
Reactアプリケーションのレビューで、特に注意して見たいのが非同期処理です。
fetch
, setTimeout
, WebSocket
, dispatch(asyncThunk)
… など、非同期のロジックがUIコンポーネント内にベタ書きされている構造は、レビューの重点ポイントになります。
この記事では、非同期処理を含む構造をどう見抜き、どう再構成すればよいのかを、レビューアー視点で具体的に解説します。
非同期処理が内包されている兆候
まずは、以下のようなコードがあれば構造的課題を疑ってよいです。
非同期処理が直接埋め込まれている例
export function UserInfo() {
const [user, setUser] = useState(null);
useEffect(() => {
fetch('/api/user')
.then(res => res.json())
.then(setUser);
@Reviewer非同期通信を直接UIコンポーネントに書いてしまうと、構造の責務が曖昧になります。専用のHookや外部の処理モジュールに切り出すことを検討しましょう。 }, []);
return <div>{user?.name}</div>;
}
- API取得は副作用
- UI層ではなくロジック層で持つべき
- 将来的なエラー処理・再取得対応が難しくなる
よくある非同期処理の混在パターン
パターン | 構造の課題 | 改善方向 |
---|---|---|
useEffect 内にfetch直書き |
UI責務とロジック責務の混在 | カスタムHookで分離 |
onClickイベントにasync 処理直書き |
UIイベントとドメイン操作の密結合 | モジュール化 or useAsyncなどの抽象化 |
Redux dispatch(asyncThunk) を直接書く | 再利用しにくく、依存関係が不明瞭 | 処理サービス層に移譲 |
構造分離の指針
パターン1:データ取得はCustom Hookへ
useUser.ts
export function useUser() {
const [user, setUser] = useState(null);
useEffect(() => {
fetch('/api/user')
.then(res => res.json())
.then(setUser);
}, []);
return user;
}
UserInfo.tsx
const user = useUser();
return <div>{user?.name}</div>;
useUser
の中で副作用を完結させ、UIから副作用を切り離す- テストや構造理解が圧倒的にしやすくなる
パターン2:非同期イベントはuseCallback+抽象モジュールへ
useSaveUser.ts
export async function saveUser(data) {
const res = await fetch('/api/save', {
method: 'POST',
body: JSON.stringify(data),
});
return await res.json();
}
ProfileForm.tsx
const handleSave = useCallback(async () => {
const result = await saveUser(form);
alert(result.status);
}, [form]);
- UIイベントはあくまでトリガーのみ
- 非同期の本体は抽象化された外部関数で定義
構造の違いを図示
構造的に非同期処理がUIから切り離されている後者の方が、責務の明確さ・拡張性・テスト容易性の面ですぐれています。
Redux環境でのレビュー観点
ReduxやRTK環境でも、dispatch(fetchUser())
のような記述が直接UIコンポーネント内にある場合、以下のような構造的問題が発生しやすいです。
- どのタイミングでdispatchされるかが隠蔽されやすい
- 複数のdispatchが並列で走って順序があいまいになる
- テスト時にstoreのモックが面倒になる
改善案
useFetchUser.ts
export const useFetchUser = () => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchUser());
}, [dispatch]);
};
UserContainer.tsx
useFetchUser();
return <UserInfo />;
このように、非同期のトリガー側をHookに閉じ込めるだけでも構造の明瞭さが増します。
レビューコメント例
非同期処理混在の指摘
@Reviewer: fetchをUI内に記述する構造は、責務の混在と再利用性の低下を招きます。useXxxの形で切り出し、処理の責務を明示的に分離しましょう。
提案コメント例
- useEffect(() => { fetch(...); }, []);
+ const user = useUser();
まとめ
Reactで非同期処理を書くのは自然なことですが、それがUI構造と一体化してしまうと、コードの可読性・保守性が大きく下がります。
レビューアーとしては、「そのfetch、このコンポーネントで書くべき?」「非同期の流れが明確か?」という視点で、
責務の分離・構造の再設計を提案していくことが重要です。
非同期ロジックこそ、構造レビューで真っ先に見抜きたい設計要素のひとつです。