Changing props of child components incorrectly

Loading

Properly Handling Child Component Props in React

A common React anti-pattern is trying to modify a child component’s props directly or using them incorrectly. Here’s how to properly work with child component props:

The Core Principle: Props Are Read-Only

Remember: Props flow down from parent to child and should never be modified by the child component. This is a fundamental React principle.

Common Mistakes and Solutions

1. Trying to Modify Props Directly (Anti-pattern)

// ❌ Wrong - never modify props directly
function ChildComponent({ value }) {
  value = value + 1; // Mutating props!
  return <div>{value}</div>;
}

Solution: Use state derived from props if you need to modify values:

// ✅ Correct - use state derived from props
function ChildComponent({ initialValue }) {
  const [value, setValue] = useState(initialValue);

  const increment = () => {
    setValue(prev => prev + 1);
  };

  return (
    <div>
      {value}
      <button onClick={increment}>Increment</button>
    </div>
  );
}

2. Not Using Key Prop When Rendering Lists

// ❌ Problematic - missing key prop
{items.map(item => <ChildComponent value={item.value} />)}

Solution: Always include a unique key:

// ✅ Correct - with key prop
{items.map(item => (
  <ChildComponent key={item.id} value={item.value} />
))}

3. Passing Too Many Props (Prop Drilling)

// ❌ Problematic - excessive prop drilling
<Parent>
  <Child>
    <GrandChild>
      <GreatGrandChild someProp={value} />
    </GrandChild>
  </Child>
</Parent>

Solutions:

  • Use React Context
  • Component composition
  • State management library (Redux, MobX, etc.)

4. Passing Objects Without Memoization

// ❌ Problematic - new object reference on each render
<ChildComponent config={{ width: 100, height: 200 }} />

Solutions:

// ✅ Solution 1 - Memoize with useMemo
const config = useMemo(() => ({ width: 100, height: 200 }), []);

// ✅ Solution 2 - Define outside component
const CONFIG = { width: 100, height: 200 };

function Parent() {
  return <ChildComponent config={CONFIG} />;
}

Best Practices for Child Component Props

  1. Treat props as immutable – Never modify them directly
  2. Use TypeScript or PropTypes – Catch prop issues early
  3. Destructure props for cleaner code:
   function ChildComponent({ title, description, onClick }) {
     // component logic
   }
  1. Default props for optional values:
   ChildComponent.defaultProps = {
     disabled: false,
     size: 'medium'
   };

Or with modern JavaScript:

   function ChildComponent({ disabled = false, size = 'medium' }) {
     // component logic
   }

Advanced Patterns

1. Controlled Components Pattern

function Parent() {
  const [value, setValue] = useState('');

  return (
    <ChildInput 
      value={value}
      onChange={(e) => setValue(e.target.value)}
    />
  );
}

function ChildInput({ value, onChange }) {
  return <input value={value} onChange={onChange} />;
}

2. Children as a Function (Render Props)

function MouseTracker({ render }) {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  const handleMouseMove = (e) => {
    setPosition({ x: e.clientX, y: e.clientY });
  };

  return <div onMouseMove={handleMouseMove}>{render(position)}</div>;
}

// Usage:
<MouseTracker render={({ x, y }) => (
  <div>Mouse position: {x}, {y}</div>
)} />

Remember that proper prop handling leads to more maintainable, predictable components that are easier to debug and test.

Leave a Reply

Your email address will not be published. Required fields are marked *