Components are functions, Props are the arguments passed into them. They allow a parent component to pass data down to a child component.
Props are Read-Only (Immutable). A child component receives props, but it cannot change them. If a child tries to modify
props.name = "New Name", React will throw an error (or fail silently). This strict rule ensures that data changes are predictable.
// Parent
<Welcome name="Sara" />;
// Child
function Welcome(props) {
// ✅ GOOD: Reading
return <h1>Hello, {props.name}</h1>;
// ❌ BAD: Writing (This will break)
// props.name = "John";
}Props are arguments, State is the component's internal variable. It is private memory that allows a component to change what it displays.
In functional components, we use the useState hook to add memory.
import { useState } from "react";
function Counter() {
// Syntax: const [currentValue, updateFunction] = useState(initialValue);
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
{/* ❌ NEVER do this: count = count + 1 */}
{/* ✅ ALWAYS do this: */}
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}setCount?Why can't we just say count = 5?
Because React needs to know that data changed so it can trigger a re-render (update the screen).
setCount(5).Counter function component.| Feature | Props | State |
|---|---|---|
| Origin | Passed from Parent component. | Created inside the component. |
| Mutability | Read-Only (Immutable). | Mutable (via setter function). |
| Scope | Can be accessed by child. | Local to the component (unless passed down). |
| Role | Configuration / Data passing. | Interactivity / Memory. |
This is a defining characteristic of React architecture.
Since a child cannot change its own props, how does it update data stored in the parent? Answer: The parent passes a function down as a prop.
// 1. Parent
function Parent() {
const [message, setMessage] = useState("Hello");
// Define a function to change state
const handleUpdate = () => setMessage("Updated by Child!");
return (
<div>
<h1>{message}</h1>
{/* Pass the function down */}
<Child onButtonClick={handleUpdate} />
</div>
);
}
// 2. Child
function Child(props) {
return (
// Call the function passed via props
<button onClick={props.onButtonClick}>Click Me</button>
);
}| Concept | Definition | Key Takeaway |
|---|---|---|
| Props | Inputs passed to a component. | Read-Only. Do not modify them. |
| State | Internal data of a component. | Mutable. Triggers re-render when changed. |
useState | Hook to manage state. | Returns [value, setter]. |
| Unidirectional Flow | Data moves Parent → Child. | Keeps the app predictable and easy to debug. |
| Lifting State Up | Moving state to a common parent. | Required when two siblings need to share data. |
User data, and a Great-Grandchild that needs to display the User Name, how do we get the data there using only Props?We must pass the data down through every single layer, even if the intermediate components (Grandparent, Parent, Child) do not need the data themselves.
Prop Drilling. It makes our code messy because the middlemen components are cluttered with props they don't use just to pass them along.setCount is often asynchronous. If we run setCount(count + 1); console.log(count); immediately after, will the console log the old number or the new number?It will log the OLD number.
Why: setCount does not update the count variable immediately in the current running function. It schedules an update for the next render.
Example:
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1); // Schedules update to 1
console.log(count); // Still prints 0!
}The Mental Model: The variable count is a constant snapshot of the state for this specific render. It cannot change while the function is running.
If children could modify props, we would lose the Single Source of Truth.