~/wiki

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-state
  • no-param-reassign
  • Custom rules for prop mutation detection

Prevention Strategies

Component Design

  1. Treat Props as Immutable: Never modify props directly
  2. Use Local State: Copy props to local state if mutation is needed
  3. Pure Functions: Use pure functions for data transformations
  4. Clear Data Flow: Make data transformations explicit and traceable

Development Practices

  1. Code Review Focus: Specifically review for prop mutation patterns
  2. Testing: Write tests that verify component behavior doesn't affect parent state
  3. Documentation: Document data flow expectations clearly
  4. Type Safety: Use TypeScript readonly modifiers where appropriate

Architecture Patterns

  1. Unidirectional Data Flow: Maintain clear parent-to-child data flow
  2. State Management: Use proper state management for shared data
  3. Event Callbacks: Use callbacks to communicate changes back to parents
  4. 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:

  1. Immediate Fix: Clone data before any transformation
  2. Data Integrity Check: Verify that upstream data hasn't been corrupted
  3. Component Audit: Review all components that share the same data
  4. Testing: Add tests to prevent regression
  5. Monitoring: Add runtime checks to detect future mutations

See also