# Modernize this React 19 data-loading flow. Identify real request races, stale state writes, cancellation weaknesses, and unnecessary Effects. Distinguish current React guidance from optional framework-level alternatives. Ground every high-priority finding in the supplied code with file and line locations. Restrict React claims to exact official pages: https://react.dev/reference/react/useEffect, https://react.dev/learn/synchronizing-with-effects, https://react.dev/learn/you-might-not-need-an-effect, https://react.dev/reference/react/useTransition, https://react.dev/reference/react/startTransition, and https://react.dev/reference/react/useOptimistic. Use https://dom.spec.whatwg.org/#aborting-ongoing-activities or the exact MDN AbortController page for abort behavior. Do not use conference pages, framework homepages, generic source pages, or unsupported citations. Do not recommend a framework migration as though it were required.

**Mode:** default  
**Runtime:** Not detected  
**Language:** TypeScript

## Direct answer

Highest priority: activity refresh, save failure, and optimistic POST callbacks can write stale data after a project switch or concurrent reload (lines 169-192, 195-224, 246-249). Abort cleanup cancels cooperative fetch work but does not itself suppress already-resolved promise callbacks. Remove the ref-sync Effect and use request/project identities; retain the loading Effect or optionally adopt a cache/framework loader.

## Findings

### Mutation concurrency is only partially protected.

**Priority:** medium  
**Location:** project-workspace.tsx:212-213  
**Evidence:** `const nextActivity = await getActivity(project.id)
    setActivity(nextActivity)`

A refresh or POST completion can write after the selected project or request ordering has changed. This is a stale-state integrity concern, not proof of a server vulnerability.

**Recommendation:** Use per-request identity and project checks, plus cancellation and ordering for refreshes and mutations. Sources: S1, S6.

### Server JSON is trusted after a compile-time cast.

**Priority:** medium  
**Location:** project-workspace.tsx:43  
**Evidence:** `return await response.json() as T`

Successful HTTP decoding does not establish that the payload matches Project or Activity.

**Recommendation:** Validate response schemas before storing data. This is an engineering judgment based on the supplied code. Sources: citation undiscovered.

### Sequential project then activity fetch creates a waterfall.

**Priority:** medium  
**Location:** project-workspace.tsx:137  
**Evidence:** `return getActivity(nextProject.id, controller.signal)`

Activity begins only after the project request resolves.

**Recommendation:** Fetch independent resources in parallel when possible, or use an optional cache/data loader. Sources: citation undiscovered.

### Manual Effect fetching prevents preload and server data loading.

**Priority:** low  
**Location:** project-workspace.tsx:117  
**Evidence:** `useEffect(() => {`

Effects do not run during server rendering and manual fetching can miss caching and preload opportunities.

**Recommendation:** Consider a cache or framework data loader only when SSR, deduplication, or initial latency matters; migration is optional. Sources: citation undiscovered.

## Upgrade path

### Prevent stale writes across project changes and races.

- Capture projectId and a monotonically increasing request token for each load, save, POST, and reload.
- Before every success and failure state write, verify both token and current project.
- Keep the existing loading cleanup abort, but do not rely on abort alone.
- Treat a concurrent reload as ordered work so an older snapshot cannot replace newer activity. Sources: citation undiscovered.

### Make all requests cancellable.

- Add signal?: AbortSignal to saveProject, postActivity, and reloadActivity.
- Pass signals to fetch.
- Use signal.aborted or the signal reason to ignore cancellation rather than only checking DOMException AbortError. Sources: S1.

### Remove unnecessary ref synchronization.

- Delete selectedProjectRef and its Effect.
- Use request-local project IDs and tokens for save guards.
- Keep the network Effect because it synchronizes with an external system. Sources: S9.

## Sources

- [S1: WHATWG DOM Standard: aborting ongoing activities](https://dom.spec.whatwg.org/#aborting-ongoing-activities) — WHATWG
- [S6: RFC 9110: HTTP Semantics](https://www.rfc-editor.org/rfc/rfc9110) — RFC Editor
- [S9: React useEffect reference](https://react.dev/reference/react/useEffect) — React
- [WEB-1: React startTransition reference](https://react.dev/reference/react/startTransition) — react.dev
