Topic 5: State and Props
📖 6 min read · 🎯 beginner · 🧭 Prerequisites: react-components, lifecycle-methods
⚠️ Note: this lesson was regenerated because the source content did not match the title.
Why this matters
Here's the thing — once you start building React apps, you'll quickly hit a wall where your components just sit there, static and lifeless. You'll ask: how do I pass data into a component? How do I make it remember something, like whether a button was clicked? That's exactly what props and state solve. Props let a parent component hand information down to a child. State lets a component track and update its own data over time. Understanding the difference between these two is the moment React starts to click.
What You'll Learn
- Understand what props are and how to pass data from a parent component to a child
- Understand what state is and how it differs from props
- Use the
useStatehook to add reactive state to a functional component - Apply
PropTypesfor runtime prop validation - Lift state up to share data between sibling components
The Analogy
Think of a vending machine in the Vizag break room. Props are like the coins you drop in — they come from outside, they determine what the machine does, and the machine itself cannot change them. State is like the internal counter that tracks how many cans are left — it lives inside the machine, it can change as drinks are dispensed, and the machine updates its display automatically every time it changes. You feed props in; the component owns its state. Mixing the two up is the most common React beginner mistake, so burning this analogy in now saves hours of debugging later.
Chapter 1: Props — Passing Data Into a Component
Props (short for properties) are the mechanism React uses to pass data downward through the component tree, from parent to child. A child component receives props as a plain JavaScript object and treats them as read-only.
Passing Props from a Parent
// App.jsx
import Greeting from './Greeting';
function App() {
return <Greeting name="Ada" role="Architect" />;
}
Receiving Props in the Child
// Greeting.jsx
function Greeting(props) {
return (
<h1>
Welcome to Vizag, {props.name}! You are a {props.role}.
</h1>
);
}
You can also destructure props directly in the function signature for cleaner code:
function Greeting({ name, role }) {
return (
<h1>
Welcome to Vizag, {name}! You are a {role}.
</h1>
);
}
Default Props
If a prop might not always be passed, provide a fallback with default parameter syntax:
function Greeting({ name = 'Citizen', role = 'Developer' }) {
return (
<h1>
Welcome to Vizag, {name}! You are a {role}.
</h1>
);
}
Passing Any JavaScript Value as a Prop
Props are not limited to strings. You can pass numbers, booleans, arrays, objects, and even functions:
function CouncilCard({ member, age, active, skills, onSelect }) {
return (
<div onClick={() => onSelect(member)}>
<p>{member} — Age {age} — {active ? 'Active' : 'Retired'}</p>
<p>Skills: {skills.join(', ')}</p>
</div>
);
}
// Usage
<CouncilCard
member="the trainer"
age={34}
active={true}
skills={['REST', 'HTTP', 'WebSockets']}
onSelect={(m) => console.log(m)}
/>
Chapter 2: Prop Validation with PropTypes
PropTypes is a runtime validation library that warns you in the browser console when a prop receives the wrong type. Install it separately (it was removed from React core in v15.5):
npm install prop-types
import PropTypes from 'prop-types';
function CouncilCard({ member, age, active }) {
return (
<div>
{member} — {age} — {active ? 'Active' : 'Retired'}
</div>
);
}
CouncilCard.propTypes = {
member: PropTypes.string.isRequired,
age: PropTypes.number.isRequired,
active: PropTypes.bool,
};
CouncilCard.defaultProps = {
active: true,
};
If member is omitted or passed a non-string value, React prints a warning in development mode — it does not throw in production.
Chapter 3: State — Data That Changes Over Time
While props flow in from outside, state is data a component manages internally. When state changes, React automatically re-renders the component to reflect the new value.
In functional components, state is created with the useState hook:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0); // initial value = 0
return (
<div>
<p>Council vote count: {count}</p>
<button onClick={() => setCount(count + 1)}>Cast Vote</button>
</div>
);
}
useState returns a two-element array:
count— the current state valuesetCount— the setter function; calling it triggers a re-render
State Is Asynchronous
React batches state updates for performance. Never rely on the current value of state inside the setter — use the functional update form when the new value depends on the old one:
// Correct — uses the previous value guaranteed by React
setCount((prev) => prev + 1);
// Risky — `count` may be stale in rapid updates
setCount(count + 1);
Multiple State Variables
You can call useState as many times as needed:
function LoginForm() {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState(null);
// ...
}
State With Objects
function TeamMember() {
const [profile, setProfile] = useState({ name: 'Scout', online: false });
function goOnline() {
// Always spread existing state — never mutate directly
setProfile((prev) => ({ ...prev, online: true }));
}
return (
<div>
<p>{profile.name} is {profile.online ? 'online' : 'offline'}</p>
<button onClick={goOnline}>Go Online</button>
</div>
);
}
Chapter 4: Props vs. State — Side by Side
| Props | State | |
|---|---|---|
| Owned by | Parent component | The component itself |
| Mutable by child? | No — read-only | Yes — via setter |
| Triggers re-render? | When parent re-renders | Yes — every setState call |
| Use case | Configure a component | Track user interaction / async data |
A helpful rule: if data comes from outside, it's a prop. If the component needs to remember something between renders, it's state.
Chapter 5: Lifting State Up
When two sibling components need to share the same piece of state, move that state up to their closest common ancestor and pass it down via props.
// Parent owns the state
function VotingPanel() {
const [votes, setVotes] = useState(0);
return (
<div>
<VoteDisplay votes={votes} />
<VoteButton onVote={() => setVotes((v) => v + 1)} />
</div>
);
}
// Child A — receives state as prop
function VoteDisplay({ votes }) {
return <p>Total votes: {votes}</p>;
}
// Child B — receives setter as prop (callback pattern)
function VoteButton({ onVote }) {
return <button onClick={onVote}>Cast Vote</button>;
}
VoteDisplay and VoteButton never talk to each other directly — all communication goes through the parent. This is the core of React's unidirectional data flow.
graph TD
VP[VotingPanel\nstate: votes] -->|votes prop| VD[VoteDisplay]
VP -->|onVote prop| VB[VoteButton]
VB -->|calls onVote| VP
🧪 Try It Yourself
Build a character card component for the Vizag council roster.
Task: Create a TeamMember component that:
- Accepts
name,title, andspecialtyas props - Tracks a local
endorsedboolean withuseState(startsfalse) - Renders a button that toggles endorsement and changes the button label between "Endorse" and "Endorsed ✓"
Starter snippet:
import { useState } from 'react';
function TeamMember({ name, title, specialty }) {
const [endorsed, setEndorsed] = useState(false);
return (
<div style={{ border: '1px solid #ccc', padding: 16, marginBottom: 8 }}>
<h2>{name}</h2>
<p>{title} — {specialty}</p>
<button onClick={() => setEndorsed((e) => !e)}>
{endorsed ? 'Endorsed ✓' : 'Endorse'}
</button>
</div>
);
}
// Use it like this in App.jsx:
// <TeamMember name="the trainer" title="Networking Ranger" specialty="REST APIs" />
Success criterion: Clicking the button toggles the label, and each TeamMember instance tracks its own endorsement independently.
🔍 Checkpoint Quiz
Q1. What does it mean that props are "read-only"?
A) Props cannot hold string values
B) A child component must not mutate the props it receives
C) Props can only be read once per render cycle
D) Props are stored in the browser's read-only memory
Q2. Given this component, what renders when the button is clicked three times?
function Counter() {
const [count, setCount] = useState(10);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount((c) => c - 3)}>Subtract</button>
</div>
);
}
A) 7
B) 1
C) 10
D) 0
Q3. What is wrong with this code?
function Badge({ label }) {
label = label.toUpperCase(); // transform before render
return <span>{label}</span>;
}
A) toUpperCase is not a valid string method in JSX
B) You cannot call functions inside a component
C) The prop label is being mutated, which violates React's read-only prop contract
D) Nothing — this is perfectly valid React
Q4. Two sibling components, ScoreBoard and ScoreButton, both need access to the same score value. Where should the state live?
A) Inside ScoreBoard, passed up to the parent via a callback
B) In a global browser variable
C) In the closest common ancestor component, passed down as props
D) Duplicated in both components and synced with useEffect
A1. B) A child component must not mutate the props it receives. Props belong to the parent; only the parent can change them by re-rendering with new values.
A2. B) 1. Starting at 10 and subtracting 3 three times: 10 → 7 → 4 → 1.
A3. C) label is a prop — reassigning it mutates the props object, violating React's contract. The correct approach is to use a local variable: const displayLabel = label.toUpperCase();.
A4. C) Lift state to the closest common ancestor. Pass score as a prop to ScoreBoard and the setter (or a callback) as a prop to ScoreButton. This is the canonical "lifting state up" pattern.
🪞 Recap
- Props are read-only data passed from parent to child; a child must never mutate them.
- State is data a component owns and can change; every
useStatesetter call triggers a re-render. - Use the functional update form
setState((prev) => ...)whenever new state depends on the previous value. - Validate incoming props at development time with the
prop-typespackage. - When siblings need shared data, lift the state up to their nearest common ancestor and pass it down via props and callbacks.
📚 Further Reading
- React Docs — Passing Props to a Component — the source of truth on prop mechanics
- React Docs — State: A Component's Memory — official deep dive into
useState - React Docs — Sharing State Between Components — the lifting-state-up pattern explained with examples
- prop-types on npm — installation and full API reference
- ⬅️ Previous: Lifecycle Methods
- ➡️ Next: Events