React Frontend Caveats
Relevant source files
The following files were used as context for generating this wiki page:
Introduction
React Frontend Caveats represent a specialized subset of bug patterns identified across multiple production projects. These caveats focus on the intricacies of the React lifecycle, state synchronization with backend data, and browser-specific API behaviors. The primary goal is to prevent common failures such as stale state synchronization, race conditions during asynchronous operations, and security vulnerabilities like DOM XSS.
Understanding these patterns is critical for maintaining data integrity between the client and server. These caveats apply to projects utilizing React hooks (useState, useEffect, useCallback, useMemo), data-fetching libraries like TanStack Query, and browser-level integrations involving DOM manipulation or protocol handoffs. Further details on general patterns can be found in Core Patterns and Critical Patterns.
Sources: BY-STACK/react-frontend.md:4-10, INTEGRATION.md:144-147, claude-md-snippets/react.md:1-2
State and Prop Synchronization
One of the most frequent issues in React frontends is the failure of local state to update when parent props change. This occurs when useState is initialized with a prop that subsequently changes due to a refetch or parent state update.
The Stale State Pattern
When a component initializes state from a prop, it only happens during the initial mount. If the prop updates later, the local state remains "stale," potentially causing the UI to display outdated information or send incorrect data back to the server.
The diagram above illustrates how the synchronization link is broken when local state is not updated following a prop change.
Sources: BY-STACK/react-frontend.md:16-25, RECURRING-PATTERNS.md:32-35
Resync Strategies
There are three primary strategies to resolve synchronization issues:
- Key Prop (Remounting): Using a unique identifier (like
id) as thekeyprop on the child component forces React to unmount and remount the component when the ID changes. - useEffect Resync: Explicitly calling a state setter within a
useEffecthook that tracks the specific prop. - Derived State: Removing local state entirely and calculating values directly from props, lifting any edits to the parent level.
Sources: BY-STACK/react-frontend.md:28-40, claude-md-snippets/react.md:4-8
Asynchronous Logic and Hooks
Asynchronous operations in React, particularly inside useEffect, introduce risks related to component unmounting and race conditions.
Cancellation Flags
Executing setState after an await without checking if the component is still mounted can lead to memory leaks or UI flickering where an old request overwrites a newer one.
A sequence diagram showing how a late response can overwrite fresh data if a cancellation mechanism is not implemented.
Sources: BY-STACK/react-frontend.md:69-79, claude-md-snippets/react.md:14-15
Rules of Hooks and Dependencies
- Hook Order: Hooks must always be declared before any early returns (e.g.,
if (!data) return null;). - Stale Closures: Variables used inside
useCallbackoruseMemomust be included in the dependency array to avoid using values from previous renders. - Identity Stability: Objects with unstable identities, such as TanStack Query mutation objects, should not be used as direct dependencies in
useEffectto prevent infinite re-render loops.
Sources: BY-STACK/react-frontend.md:55-67, claude-md-snippets/react.md:10-13, BY-STACK/react-frontend.md:83-87
Browser API Integration
React frontends often interact with low-level browser APIs that have non-obvious behaviors.
Protocol Handoffs and window.open
Using window.open() with system protocols (e.g., mailto:, tel:) returns null in browsers like Chrome. This is often misinterpreted as a popup blocker failure. The recommended fix is using an <a> element with a .click() method for these specific protocols.
Sources: BY-STACK/browser-handoff.md:12-25, RECURRING-PATTERNS.md:95-98
Critical Browser Caveats Table
| API / Feature | Problem | Solution |
|---|---|---|
navigator.clipboard | Fails on HTTP (requires HTTPS) | Use try/catch and fallback to legacy methods |
URL.createObjectURL | Memory leaks (Blob URLs persist) | Call URL.revokeObjectURL in useEffect cleanup |
setTimeout / Date | Crashes on NaN or non-finite inputs | Validate inputs with Number.isFinite() |
innerHTML | DOM XSS Vulnerability | Use textContent or DOMPurify.sanitize() |
localStorage | Non-atomic updates for auth pairs | Store as a single JSON blob or update both in a try block |
Sources: BY-STACK/browser-handoff.md:33-75, claude-md-snippets/react.md:16-24, bugbot-rules/xss-innerhtml.md:8-15
Security and DOM Manipulation
Directly manipulating the DOM or rendering raw HTML from external sources is a high-severity risk.
DOM XSS via innerHTML
The use of innerHTML, dangerouslySetInnerHTML, or similar sinks with strings derived from API responses, URL parameters, or user input can lead to Cross-Site Scripting (XSS).
Conclusion
React Frontend Caveats emphasize that the complexity of modern interfaces lies in the synchronization between state, asynchronous side effects, and the browser's security policies. By adhering to patterns such as cancellation flags, explicit resync strategies, and sanitizing external inputs, developers can avoid the most common causes of data corruption and UI instability in React applications.
Sources: BY-STACK/react-frontend.md:108-115, MIGRATION-NOTES.md:65-72