Warming up the neural circuits...
(short for "properties") are how you pass data from a parent component to a child component. They're like function arguments for components.
// Parent passes data
function App() {
return <Greeting name="Alice" age={25} isLoggedIn={true} />;
}
// Child receives props as an object
function Greeting(props) {
return (
<div>
<h1>Hello, {props.name}!</h1>
<p>You are {props.age} years old.</p>
<p>Status: {props.isLoggedIn ? "Online" : "Offline"}</p>
</div>
);
}function Greeting({ name, age, isLoggedIn }) {
return (
<div>
<h1>Hello, {name}!</h1>
<p>You are {age} years old.</p>
</div>
function Greeting({ name = "Guest", age = 0 }) {
return <h1>Hello, {name} (age {age})</h1>;
}function BadComponent({ count }) {
count = count + 1; // ❌ Never modify props!
return <p>{count}</p>;
}Props are immutable. A component should never modify its own props. If you need to change data, use .
App (parent)
│
├── Header title="My App"
├── ProfileCard user={userData}
│ ├── Avatar url={user.avatar}
│ └── Bio text={user.bio}
└── Footer year={2026}Data flows one way: parent → child. This is called unidirectional data flow.
State is data that changes over time. When state changes, React re-renders the component to reflect the new data.
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p
const [state, setState] = useState(initialValue);useState(0) returns [0, setCount]setCount(count + 1) is calleduseState(0) returns [1, setCount] — the new statefunction Counter() {
const [count, setCount] = useState(0);
// ❌ Wrong — React won't re-render
const badIncrement = () => { count = count + 1; };
// ✅ Right — use the setter function
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
console.log(count); //
Each component instance has its own state — clicking one Counter doesn't affect another.
When two components need to share the same state, lift the state up to their closest common ancestor:
function Parent() {
const [sharedData, setSharedData] = useState("");
return (
<div>
<ChildA data={sharedData} onUpdate={setSharedData} />
<ChildB
| Props | State | |
|---|---|---|
| Who owns it? | Parent component | The component itself |
| Can it change? | No (immutable) | Yes (via setter) |
| Purpose | Configure child, pass data | Track changing data |
| Re-renders on change? | Yes (if parent re-renders) | Yes |
Mistake 1: Modifying state directly
const [items, setItems] = useState([1, 2, 3]);
items.push(4); // ❌ Direct mutation!
setItems([...items, 4]); // ✅ New arrayMistake 2: Calling setter in render (infinite loop)
function Bad() {
const [count, setCount] = useState(0);
setCount(count + 1); // ❌ Infinite loop!
return <p>{count}</p>;
}useState