Prop Mutation Antipattern
Mis à jour le 2026-06-11Confiance : high
react-antipatternsprop-mutationimmutabilitycomponent-designfrontend-bugsdata-corruption
A critical React antipattern where components directly modify props instead of treating them as immutable. This violation of React's unidirectional data flow can cause subtle bugs, unpredictable behavior, and data corruption across component trees.
The Problem
React props should be treated as read-only. When components mutate props directly, they break the expectation that data flows unidirectionally from parent to child components.
Example from Archipel Kombucha
// Problematic code in ClientTable component
const memoizedFilterPipeline = useMemo(() => {
// ... filtering logic
result.sort((a, b) => a.name.localeCompare(b.name)); // Direct prop mutation
return result;
}, [clients, searchTerm, sortConfig]);
In this case, the clients prop is directly sorted, permanently reordering the server-fetched data for all components sharing this reference.
Why This is Dangerous
Data Corruption
- Global State Changes: Mutations affect all components sharing the same data reference
- Unpredictable Behavior: Components may see different data depending on render order
- Cascade Effects: One component's sorting can permanently affect another component's display
Debugging Challenges
- Non-Obvious Bugs: Effects may appear in unrelated components
- Time-Dependent Issues: Bugs may only appear after specific user interaction sequences
- Hard to Reproduce: Issues may depend on component mounting and update order
Performance Issues
- Unnecessary Re-renders: Mutations can trigger unexpected component updates
- Memoization Breaking: Mutated references break React's optimization assumptions
- Memory Leaks: Retained references to mutated objects can prevent garbage collection
Correct Approaches
Immutable Updates
// Correct: Clone before mutation
const sortedClients = useMemo(() => {
const filteredClients = clients.filter(/* ... */);
return [...filteredClients].sort((a, b) => a.name.localeCompare(b.name));
}, [clients, searchTerm, sortConfig]);
Immutability Libraries
import produce from 'immer';
const sortedClients = useMemo(() =>
produce(clients, draft => {
draft.sort((a, b) => a.name.localeCompare(b.name));
})
, [clients, searchTerm]);
Functional Programming Patterns
// Using pure functions for transformations
const sortClients = (clients: Client[], compareFn: (a: Client, b: Client) => number) =>
[...clients].sort(compareFn);
const filteredAndSorted = useMemo(() =>
sortClients(
clients.filter(/* filter logic */),
(a, b) => a.name.localeCompare(b.name)
)
, [clients, filterCriteria]);
Detection Strategies
Code Review Patterns
Look for these warning signs:
- Direct array methods on props:
props.data.sort(),props.items.push() - Object property assignments:
props.user.name = newName - Destructuring followed by mutation:
const { items } = props; items.reverse()
Runtime Detection
// Development-only prop validation
const validatePropImmutability = (props: any) => {
if (process.env.NODE_ENV === 'development') {
Object.freeze(props);
if (Array.isArray(props.data)) {
Object.freeze(props.data);
}
}
};
ESLint Rules
Configure ESLint rules to catch common mutation patterns:
react/no-direct-mutation-stateno-param-reassign- Custom rules for prop mutation detection
Prevention Strategies
Component Design
- Treat Props as Immutable: Never modify props directly
- Use Local State: Copy props to local state if mutation is needed
- Pure Functions: Use pure functions for data transformations
- Clear Data Flow: Make data transformations explicit and traceable
Development Practices
- Code Review Focus: Specifically review for prop mutation patterns
- Testing: Write tests that verify component behavior doesn't affect parent state
- Documentation: Document data flow expectations clearly
- Type Safety: Use TypeScript readonly modifiers where appropriate
Architecture Patterns
- Unidirectional Data Flow: Maintain clear parent-to-child data flow
- State Management: Use proper state management for shared data
- Event Callbacks: Use callbacks to communicate changes back to parents
- Immutable Data Structures: Consider libraries like Immutable.js for complex data
Impact on Performance
Positive Effects of Immutability
- Predictable Re-renders: React can optimize rendering with stable references
- Memoization Benefits: useMemo and React.memo work correctly
- Shallow Comparison: Efficient equality checks for optimization
Common Performance Traps
- Unnecessary Cloning: Don't clone data if no mutation occurs
- Deep Cloning: Avoid expensive deep clones for simple transformations
- Reference Stability: Maintain stable references when data doesn't change
Framework Considerations
React Specific
- Props are read-only by design
- State updates should use functional updates
- useCallback and useMemo assume immutable data
Other Frameworks
- Vue.js: Props should not be mutated
- Angular: OnPush change detection assumes immutable inputs
- Svelte: Reactive statements expect immutable updates
Recovery Strategies
When prop mutation is discovered in production:
- Immediate Fix: Clone data before any transformation
- Data Integrity Check: Verify that upstream data hasn't been corrupted
- Component Audit: Review all components that share the same data
- Testing: Add tests to prevent regression
- Monitoring: Add runtime checks to detect future mutations
See also
- client-side-performance-optimization
- React Component Design Patterns
- Data Flow Architecture
- Immutable Data Structures
- Archipel Kombucha Project