![]()
A common React confusion arises when deciding whether to use props or state for managing data that changes within a component. Here’s a clear guide to help you make the right choice.
The Golden Rule
Use props for data that comes from the parent and doesn’t change within the component.
Use state for data that’s local to the component and needs to change over time.
When to Use State Instead of Props
1. For Local UI State
// ❌ Incorrect - using props for local toggle
function ToggleButton({ isOn }) {
return (
<button onClick={() => isOn = !isOn}> // Won't work!
{isOn ? 'ON' : 'OFF'}
</button>
);
}
// ✅ Correct - using local state
function ToggleButton() {
const [isOn, setIsOn] = useState(false);
return (
<button onClick={() => setIsOn(!isOn)}>
{isOn ? 'ON' : 'OFF'}
</button>
);
}
2. For Form Inputs
// ❌ Problematic - trying to mutate props directly
function InputField({ value }) {
return <input value={value} onChange={e => value = e.target.value} />;
}
// ✅ Correct - using local state
function InputField() {
const [value, setValue] = useState('');
return (
<input
value={value}
onChange={e => setValue(e.target.value)}
/>
);
}
When to Derive State From Props
Sometimes you need to initialize state from props but still maintain local control:
function Counter({ initialCount }) {
// Initialize from props but manage locally
const [count, setCount] = useState(initialCount);
return (
<div>
<button onClick={() => setCount(c => c - 1)}>-</button>
<span>{count}</span>
<button onClick={() => setCount(c => c + 1)}>+</button>
</div>
);
}
Advanced Pattern: Fully Controlled Component
If you need the parent to control the state but still want local updates:
function ControlledInput({ value, onChange }) {
const [localValue, setLocalValue] = useState(value);
useEffect(() => {
setLocalValue(value);
}, [value]);
const handleChange = (e) => {
const newValue = e.target.value;
setLocalValue(newValue);
onChange(newValue);
};
return <input value={localValue} onChange={handleChange} />;
}
Key Differences
| Aspect | Props | State |
|---|---|---|
| Ownership | Parent component | Current component |
| Mutability | Immutable (read-only) | Mutable |
| Updates | Parent controls | setState or useState setter |
| Use case | Configuration from parent | Internal component data |
Best Practices
- Lift state up when multiple components need to share the same data
- Keep state local when it’s only relevant to one component
- Avoid deriving state from props unless necessary (can lead to sync issues)
- Use controlled components when parent needs to manage child’s state
- Consider context for deeply nested state that’s used in many components
Remember: If the data changes within the component and doesn’t affect other components, it should be state. If it’s passed down from a parent and shouldn’t change, it should be props.
