[← Back to Reviews Index](../Stewards%20Reviews%20Index.md)

# React Dependency Injection Review — Azure-AI-RAG-CSharp-Semantic-Kernel-Functions

**Steward:** React DI Steward
**Project:** Azure-AI-RAG-CSharp-Semantic-Kernel-Functions
**Run Date:** 2026-03-21
**Scope:** `src/web/src/` — React frontend only

---

## 1. Dependency Injection Architecture Overview

The React frontend is a small, single-page Create React App (CRA) application. The component tree is shallow:

```
index.js
  └── App.js          (router shell)
        └── Main.js   (class component)
              ├── AzureFreeAccount.js   (static UI, no dependencies)
              ├── AzureServiceCards.js  (static UI, no dependencies)
              └── SupportAgent/Agent.js (functional component, owns API calls)
                    └── SupportAgent/ChatLayout.js (presentational)
```

The application has no DI infrastructure whatsoever: no React Context providers, no custom hooks, no service modules, and no environment-abstraction layer. All external interaction (two `fetch` calls to the backend API) is embedded directly inside the `Agent.js` component. Configuration (`REACT_APP_API_HOST`) is accessed directly via `process.env` inside the component body.

Because the app is a simple demo with only two API endpoints and four active component files, the scope of DI concerns is limited but the existing patterns establish anti-patterns that would compound as the application grows.

---

## 2. Context API Assessment

**Finding: No Context API is used anywhere in the application.**

There are no `React.createContext`, `Provider`, or `useContext` calls in any file. For the current scale of the application this is not immediately harmful, but it means:

- The API host URL (`process.env.REACT_APP_API_HOST`) is accessed in-place within `Agent.js` rather than via a config context.
- Session state and chat messages are local to `Agent.js` with no sharing mechanism if a second consumer were ever needed.
- There is no auth context, theme context, or any other cross-cutting concern provider.

**Assessment:** Not applicable at the current scale (one API-consuming component), but the absence means the codebase has no established pattern for introducing shared concerns cleanly.

---

## 3. Prop Drilling Assessment

**Finding: No prop drilling beyond 2 levels. The depth limit (>3) is not violated.**

The deepest prop chain in the application is:

```
Agent.js  →  ChatLayout.js
```

`Agent.js` passes a `messages` array to `ChatLayout`, which is a direct parent-child relationship (1 level). No props are threaded through intermediate components. This is appropriate and correct for the current tree depth.

No additional prop drilling findings are raised.

---

## 4. Service Instantiation Assessment

**Finding: `fetch` API calls are embedded directly inside the component with no service abstraction.**

`Agent.js` (lines 29–44 and 48–52) performs two direct `fetch` calls inline within event handlers and a `useEffect` hook. While `fetch` itself is a browser global and is not instantiated with `new`, the calls are:

1. Directly embedded in the component file — the component both owns API communication logic and UI rendering.
2. Not wrapped in any module-level function, service object, or hook.
3. Using `process.env.REACT_APP_API_HOST` read inline each time — no central URL resolution.

There are no `new ApiClient()` or similar class instantiations in render functions, so the critical-severity pattern (object construction per render) is not present. However, the fetch calls are also not guarded — if `Agent.js` triggers a re-render before the in-flight `fetch` completes, there is no abort controller to cancel the stale request.

---

## 5. Custom Hooks Assessment

**Finding: No custom hooks exist. API logic is directly embedded in the component.**

The application has no custom hooks. Both API interactions — `handleSession` (GET `/session`) and `handlePrompt` (POST `/chat`) — live inline in `Agent.js`. Extracting these into a custom hook (e.g., `useChatSession`) would:

- Separate API communication concern from UI rendering concern.
- Make the component testable by mocking the hook's return value.
- Make the fetch logic reusable if a second consumer is added.

Currently the hook layer is entirely absent, which is the primary testability barrier.

---

## 6. Testability Assessment

**Finding: `Agent.js` is untestable without real network calls.**

The single test file `App.test.js` renders `<App />` and asserts for `"learn react"` — text that does not exist in the application. This test will fail on execution and demonstrates the test suite was left in its CRA scaffold state without being updated to reflect actual application content.

More critically, `Agent.js` cannot be meaningfully unit-tested because:

1. `fetch` is called directly in `handleSession` via `useEffect` on mount — any render in a test immediately triggers a network call.
2. There is no way to inject a mock fetch, mock service, or mock context without patching the global `window.fetch`.
3. The session ID and chat messages live entirely in local state with no external seam.

`ChatLayout.js` is a pure presentational component and is straightforwardly testable by passing a `messages` prop — this is the one positive testability finding.

---

## 7. Findings

### RDI-HOOK-001 — No custom hook abstraction for API calls

| Field | Value |
|---|---|
| **ID** | RDI-HOOK-001 |
| **Severity** | Notable |
| **File** | `src/web/src/SupportAgent/Agent.js` |
| **Lines** | 22–53 |

`Agent.js` embeds both `handleSession` (GET `/session`) and `handlePrompt` (POST `/chat`) as inline component functions. There is no `useChatSession` hook or equivalent abstraction. This couples the API communication logic directly to the rendering component, making independent testing and reuse impossible.

**Recommendation:** Extract fetch logic into a custom hook `useChatSession()` that returns `{ session, messages, sendPrompt }`. The hook accepts no external dependencies initially but can later accept a `fetcher` argument for test injection.

---

### RDI-TEST-001 — Component is untestable without real network calls

| Field | Value |
|---|---|
| **ID** | RDI-TEST-001 |
| **Severity** | Notable |
| **File** | `src/web/src/SupportAgent/Agent.js` |
| **Lines** | 56–58 |

`useEffect(() => { if (session === '') handleSession() }, [])` fires immediately on mount. Because `fetch` is called directly with no injectable seam, any test that renders `<SupportAgent />` will issue a real HTTP request (or fail with a network error). The component cannot be rendered in isolation in a test environment without globally patching `window.fetch`.

**Recommendation:** Extract fetch calls into a `useChatSession` hook. In tests, mock the hook module (`jest.mock('./useChatSession')`) to provide controlled state without network access.

---

### RDI-TEST-002 — Existing test is broken and uses stale CRA scaffold assertion

| Field | Value |
|---|---|
| **ID** | RDI-TEST-002 |
| **Severity** | Notable |
| **File** | `src/web/src/App.test.js` |
| **Lines** | 4–7 |

`App.test.js` searches for the text `"learn react"` which was part of the default CRA scaffold and does not exist anywhere in the actual application. This test will fail when run, indicating the test suite has never been updated to cover actual application behavior. There are no tests for `SupportAgent`, `ChatLayout`, `AzureFreeAccount`, or `AzureServiceCards`.

**Recommendation:** Replace the scaffold test with a meaningful smoke test of the actual `App` component tree. Add targeted tests for `ChatLayout` (pure component, easily testable) and `SupportAgent` (requires network mocking or hook abstraction first per RDI-HOOK-001).

---

### RDI-CONFIG-001 — API base URL read inline from `process.env` with no central abstraction

| Field | Value |
|---|---|
| **ID** | RDI-CONFIG-001 |
| **Severity** | Minor |
| **File** | `src/web/src/SupportAgent/Agent.js` |
| **Lines** | 29, 48 |

`process.env.REACT_APP_API_HOST` is accessed directly at two call sites inside the component. If the base URL or routing logic ever changes, every call site must be updated. There is no centralized API configuration or base URL resolution function.

**Recommendation:** Define a module-level constant or a small config module (`src/config.js`) that exports `API_HOST`. Alternatively, the `useChatSession` hook recommended in RDI-HOOK-001 should own the URL resolution internally, removing the environment variable read from the component body.

---

### RDI-FETCH-001 — No abort controller on in-flight fetch requests

| Field | Value |
|---|---|
| **ID** | RDI-FETCH-001 |
| **Severity** | Minor |
| **File** | `src/web/src/SupportAgent/Agent.js` |
| **Lines** | 29–44, 48–52 |

Neither `handleSession` nor `handlePrompt` creates an `AbortController` to cancel in-flight requests on component unmount or when a new request supersedes an old one. If the component unmounts before a fetch completes, `setMessage` or `setSession` will be called on an unmounted component, causing a React state update warning.

**Recommendation:** Use `AbortController` inside `useEffect` and the event handler, or handle this automatically inside the `useChatSession` hook on cleanup.

---

### RDI-CONTEXT-001 — No React Context used; no established pattern for cross-cutting concerns

| Field | Value |
|---|---|
| **ID** | RDI-CONTEXT-001 |
| **Severity** | Info |
| **File** | `src/web/src/` (project-wide) |

The application has no Context API usage. At the current scale (one API-consuming component) this is not harmful. However, if the application grows, there is no established pattern for providing shared services (API client, auth token, config) to components without prop drilling or repeated inline access.

**Recommendation:** As the application grows, introduce a `ApiClientContext` or `AppConfigContext` that provides the API base URL and fetch wrapper. This would also serve as the injection point for test mocking.

---

### RDI-ARCH-001 — Positive: `ChatLayout` is a clean, dependency-free presentational component

| Field | Value |
|---|---|
| **ID** | RDI-ARCH-001 |
| **Severity** | Info |
| **File** | `src/web/src/SupportAgent/ChatLayout.js` |

`ChatLayout.js` accepts all data through props (`messages`) and has no side effects, context access, or service dependencies. It is straightforwardly testable and follows the ideal presentational component pattern. This separation of rendering from data-fetching is a correct structural decision.

---

## 8. Recommended Improvements

Listed in priority order:

### Priority 1 — Extract a `useChatSession` hook (addresses RDI-HOOK-001, RDI-TEST-001, RDI-CONFIG-001, RDI-FETCH-001)

Create `src/web/src/SupportAgent/useChatSession.js` that encapsulates:
- Session initialization fetch (GET `/session`)
- Chat message send fetch (POST `/chat`)
- `AbortController` cleanup on unmount
- API URL resolution from config

`Agent.js` then becomes a thin UI shell consuming the hook's returned state and actions. The hook can accept an optional `fetcher` argument (defaulting to `window.fetch`) enabling unit tests to inject a mock.

### Priority 2 — Fix and expand the test suite (addresses RDI-TEST-002)

- Update `App.test.js` to assert on actual content rendered by the application.
- Add `ChatLayout.test.js` — render with a `messages` array and assert bubble content renders correctly.
- Add `Agent.test.js` once the hook is extracted — mock `useChatSession` and verify the UI responds correctly to its return values.

### Priority 3 — Centralize API configuration (addresses RDI-CONFIG-001)

Introduce `src/web/src/config.js`:
```js
export const API_HOST = process.env.REACT_APP_API_HOST ?? '';
```
Import `API_HOST` from this module wherever needed. This provides a single location to change URL resolution logic and makes it easy to override in tests via module mocking.

### Priority 4 — Introduce a Context provider as the application grows (addresses RDI-CONTEXT-001)

When a second API-consuming component is introduced, create an `ApiClientContext` that provides the fetch wrapper and base URL. This avoids duplicating `process.env` reads and provides a clean injection point for test environments.

---

## Summary

| Severity | Count |
|---|---|
| Critical | 0 |
| Notable | 3 |
| Minor | 2 |
| Info | 2 |
| **Total** | **7** |

The React frontend is a minimal demo-grade application. The most impactful structural issue is the absence of any hook abstraction around API communication in `Agent.js`, which makes the primary interactive component untestable without network access. The existing test suite is broken (stale CRA scaffold assertion) and provides zero coverage of actual application behavior. Addressing RDI-HOOK-001 and RDI-TEST-002 in tandem would bring the project to a maintainable baseline.
