Questions
This component is trying to modify a prop directly. How do you fix it?
A developer has a Greeting component that receives a name prop (e.g., “Alice”). They want to allow the user to edit this name directly within the Greeting component using an input field.
They tried to implement this by adding an input field and updating the name prop using setName(event.target.value). However, the application is crashing or showing a warning about trying to modify a read-only property.
The Challenge
You’ve been given the App and Greeting components.
- Explain the fundamental difference between state and props in React.
- Fix the component to allow the user to edit the name correctly, adhering to React’s principles.
import React, { useState } from 'react';
// The Greeting component receives a name prop
function Greeting({ name }) {
// THE BUG: Trying to modify a prop directly.
// Props are read-only! This will cause an error or warning.
const handleNameChange = (event) => {
// This line is problematic:
// name = event.target.value; // Cannot assign to read only property 'name' of object
console.log("Attempting to change prop directly:", event.target.value);
};
return (
<div style={{ border: '1px solid #ccc', padding: '20px', margin: '10px' }}>
<h1>Hello, {name}!</h1>
<label>
Edit Name:
<input type="text" value={name} onChange={handleNameChange} />
</label>
<p>Try typing in the input field.</p>
</div>
);
}
export default function App() {
// The App component currently just passes a static name
return (
<div style={{ padding: '20px' }}>
<Greeting name="Alice" />
</div>
);
} The Solution
// The Greeting component receives a name prop function Greeting({ name }) { const handleNameChange = (event) => { // THE BUG: Trying to modify a prop directly name = event.target.value; // Cannot assign to read only property 'name' console.log("Attempting to change prop directly:", event.target.value); }; return ( <div style={{ border: '1px solid #ccc', padding: '20px', margin: '10px' }}> <h1>Hello, {name}!</h1> <label> Edit Name: <input type="text" value={name} onChange={handleNameChange} /> </label> </div> ); } export default function App() { return ( <div style={{ padding: '20px' }}> <Greeting name="Alice" /> </div> ); }
// The Greeting component now receives the name and a function to change it function Greeting({ name, onNameChange }) { return ( <div style={{ border: '1px solid #ccc', padding: '20px', margin: '10px' }}> <h1>Hello, {name}!</h1> <label> Edit Name: {/* FIX: Input value is controlled by the 'name' prop, and changes are requested via 'onNameChange' prop. */} <input type="text" value={name} onChange={onNameChange} /> </label> </div> ); } export default function App() { // FIX: The 'name' is now managed as state in the parent component (App). const [userName, setUserName] = useState('Alice'); const handleUserNameChange = (event) => { setUserName(event.target.value); }; return ( <div style={{ padding: '20px' }}> {/* FIX: Pass the state and the setter function (or a wrapper) as props. */} <Greeting name={userName} onNameChange={handleUserNameChange} /> </div> ); }
Why This Works
The core issue here is a misunderstanding of the fundamental difference between state and props in React:
- Props (Properties):
- Are passed from a parent component to a child component.
- Are read-only within the child component. A child component should never attempt to modify its props directly.
- Are used to configure a component and pass data down the component tree.
- State:
- Is managed internally within a component.
- Is mutable and can change over time.
- Is used for data that a component needs to keep track of and that might change (e.g., user input, toggled visibility, fetched data).
In the buggy code, the Greeting component receives name as a prop. Trying to assign a new value to name within handleNameChange directly violates the immutability of props, leading to an error.
The Fix: Lifting State Up and Passing a Callback
To allow the name to be editable, the name data must be managed as state by the component that “owns” it (typically the closest common ancestor that needs to manage or react to its changes). The child component then receives the current value as a prop and a callback function (also as a prop) to request changes from the parent.
Now, the App component owns the userName state. It passes the current userName as a prop to Greeting and also passes a handleUserNameChange function. When the user types in the input, Greeting calls handleUserNameChange, which updates the userName state in App. This triggers a re-render of App and Greeting with the new userName, correctly updating the UI.
Practice Question
Which statement accurately describes the mutability of state and props in React?