L1: Converting Listings to State
Transform static data into React state using the useState hook
In Module 1, we built StaySense with hardcoded listings data. Now it's time to make our application dynamic and interactive by introducing React state.
📝 Data Structure Update
Before we begin, note that our listing data structure has evolved:
Module 1 (Learning basics):
- Simple properties:
id,title,price,location,image,rating
Module 2+ (Building real app):
- Added:
guests,bedrooms(needed for booking functionality) - Removed:
rating(we'll add a complete review system in Module 7)
This is normal in real projects - data structures evolve as features grow! You don't need to change your Module 1 code. We're just expanding the data model for our booking app.
Why Do We Need State?
Right now, our HomePage component has a static array of listings:
const listings = [
{
id: 1,
title: "Cozy Beach House",
price: 250,
location: "Malibu, CA"
},
// More listings...
];Problem: This data never changes. We can't:
- Filter listings based on search criteria
- Add or remove listings dynamically
- Update prices or availability
- Make the app respond to user interactions
Solution: Convert the array to React state using the useState hook.
Key Concept: State is data that can change over time. When state changes, React automatically re-renders your component to reflect the new data.
Understanding useState
The useState hook is React's way of giving components memory:
import { useState } from 'react';
function Component() {
const [value, setValue] = useState(initialValue);
// value: current state value
// setValue: function to update state
// initialValue: starting value
return <div>{value}</div>;
}Key Points:
- Import from React:
useStateis a named export - Array Destructuring: Returns [currentValue, updaterFunction]
- Initial Value: Only used on first render
- Updater Function: Always starts with "set" by convention
Important: The updater function doesn't modify the existing state - it replaces it with a new value. State is immutable.
Step-by-Step Implementation
Step 1: Import useState
Open src/pages/HomePage.jsx and add the useState import:
import { useState } from 'react';
import PropertyCard from '../components/PropertyCard';
export function HomePage() {
// Component code...
}Why at the top? All React hooks must be imported before they're used.
Step 2: Replace Static Array with State
Find the hardcoded listings array and convert it to state:
export function HomePage() {
// ❌ Old: Static array (can't change)
const listings = [
{
id: 1,
title: "Cozy Beach House",
price: 250,
location: "Malibu, CA",
image: "/images/beach-house.jpg",
},
{
id: 2,
title: "Mountain Cabin",
price: 180,
location: "Aspen, CO",
image: "/images/cabin.jpg",
},
{
id: 3,
title: "Downtown Loft",
price: 320,
location: "New York, NY",
image: "/images/loft.jpg",
},
];
// ✅ New: State (can be updated dynamically)
const [listings, setListings] = useState([
{
id: 1,
title: "Cozy Beach House",
price: 250,
location: "Malibu, CA",
image: "/images/beach-house.jpg",
},
{
id: 2,
title: "Mountain Cabin",
price: 180,
location: "Aspen, CO",
image: "/images/cabin.jpg",
},
{
id: 3,
title: "Downtown Loft",
price: 320,
location: "New York, NY",
image: "/images/loft.jpg",
},
]);
return (
<div className="container mx-auto px-4 py-8">
<h1 className="text-3xl font-bold mb-8">Available Stays</h1>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{listings.map(listing => (
<PropertyCard key={listing.id} listing={listing} />
))}
</div>
</div>
);
}What Changed?
const listings = [...]→const [listings, setListings] = useState([...])- Same data, now managed by React state
setListingsfunction ready for future updates
Step 3: Verify Nothing Broke
Save the file and check your browser. The page should look exactly the same.
Good Sign! We've refactored to state without breaking the UI. The component still renders the same listings.
Why no visible change?
We converted to state but haven't used the setListings function yet. The listings display the same because we passed the same initial data.
Understanding State vs. Regular Variables
Why use state instead of a regular variable? Let's compare:
❌ Regular Variable (Doesn't Work)
function Component() {
let count = 0; // Regular variable
const increment = () => {
count = count + 1; // Value changes...
console.log(count); // Logs the new value...
// But UI doesn't update! ❌
};
return (
<div>
<p>{count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}Problem: Changing count doesn't tell React to re-render. The UI stays frozen at 0.
✅ State Variable (Works Perfectly)
import { useState } from 'react';
function Component() {
const [count, setCount] = useState(0); // State variable
const increment = () => {
setCount(count + 1); // Update state
// React re-renders with new value ✅
};
return (
<div>
<p>{count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}Solution: Calling setCount tells React to re-render with the new value.
React's Golden Rule: Components re-render when:
- State changes (via setState functions)
- Props change (parent passes new props)
- Parent re-renders (React re-renders children)
Complete HomePage Code
Here's your complete HomePage component with state:
import { useState } from 'react';
import PropertyCard from '../components/PropertyCard';
export function HomePage() {
const [listings, setListings] = useState([
{
id: 1,
title: "Cozy Beach House",
price: 250,
location: "Malibu, CA",
image: "/images/beach-house.jpg",
guests: 4,
bedrooms: 2,
bathrooms: 2,
},
{
id: 2,
title: "Mountain Cabin",
price: 180,
location: "Aspen, CO",
image: "/images/cabin.jpg",
guests: 6,
bedrooms: 3,
bathrooms: 2,
},
{
id: 3,
title: "Downtown Loft",
price: 320,
location: "New York, NY",
image: "/images/loft.jpg",
guests: 2,
bedrooms: 1,
bathrooms: 1,
},
]);
return (
<div className="container mx-auto px-4 py-8">
<h1 className="text-3xl font-bold mb-8">Available Stays</h1>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{listings.map(listing => (
<PropertyCard key={listing.id} listing={listing} />
))}
</div>
</div>
);
}State Management Best Practices
1. Naming Convention
Always name state variables and their updaters consistently:
const [user, setUser] = useState(null);
const [count, setCount] = useState(0);
const [isLoading, setIsLoading] = useState(false);
const [items, setItems] = useState([]);
// Pattern: [noun, setNoun] or [isCondition, setIsCondition]2. Initialize with Correct Data Type
const [count, setCount] = useState(0); // Number
const [name, setName] = useState(''); // String
const [isOpen, setIsOpen] = useState(false); // Boolean
const [items, setItems] = useState([]); // Array
const [user, setUser] = useState(null); // Object (or null)3. Don't Mutate State Directly
// ❌ Wrong: Mutating state
const [items, setItems] = useState([1, 2, 3]);
items.push(4); // Don't do this!
setItems(items);
// ✅ Correct: Create new array
setItems([...items, 4]); // Create new array with spreadRemember: State is immutable. Always create new objects/arrays.
Checkpoint
What's Next?
In Lesson 2, we'll create the ListingFilters component with search input, date pickers, and guest controls. You'll learn how to handle user input and manage multiple pieces of state.
Coming up:
- Event handlers (
onChange,onClick) - Controlled inputs
- Multiple state variables
- Component communication
Great job converting to state! 🎉