Professional React · Lesson 20 of 21
Thinking in React
From a design to components: split, build static, find minimal state, place it.
Before writing code, follow the steps the React team recommends:
1. Break the design into components: each thing that does one job. 2. Build a static version with props only, no state. 3. Find the minimal state: for each value ask: does it change? If so, can it be computed from something else? 4. Decide where each piece of state lives: the closest common parent of everything that needs it. 5. Add inverse data flow: children call the parent's functions.
- CourseCatalog (state: query, onlyFree)
- SearchBar (query, onlyFree, onChange…)
- CourseTable (courses, query, onlyFree)
- TrackRow (track name)
- CourseRow (one course)
A course catalogue page after splitting it.
const COURSES = [
{ track: "Web", title: "HTML & CSS", price: 0 },
{ track: "Web", title: "JavaScript", price: 450 },
{ track: "Web", title: "React", price: 600 },
{ track: "Data", title: "Python", price: 400 },
{ track: "Data", title: "SQL", price: 300 },
]
function SearchBar({ query, onlyFree, onQuery, onOnlyFree }) {
return (
<div>
<input value={query} onChange={(e) => onQuery(e.target.value)} placeholder="Search…" />
<label><input type="checkbox" checked={onlyFree} onChange={(e) => onOnlyFree(e.target.checked)} /> Free only</label>
</div>
)
}
function CourseTable({ courses, query, onlyFree }) {
const visible = courses.filter((c) => c.title.toLowerCase().includes(query.toLowerCase()) && (!onlyFree || c.price === 0))
const tracks = [...new Set(visible.map((c) => c.track))]
if (visible.length === 0) return <p>No courses match.</p>
return (
<table>
<tbody>
{tracks.map((t) => (
<Fragment key={t}>
<tr><th colSpan={2} style={{ textAlign: "start" }}>{t}</th></tr>
{visible.filter((c) => c.track === t).map((c) => (
<tr key={c.title}><td>{c.title}</td><td>{c.price || "Free"}</td></tr>
))}
</Fragment>
))}
</tbody>
</table>
)
}
function App() {
const [query, setQuery] = useState("")
const [onlyFree, setOnlyFree] = useState(false)
return (
<div>
<SearchBar query={query} onlyFree={onlyFree} onQuery={setQuery} onOnlyFree={setOnlyFree} />
<CourseTable courses={COURSES} query={query} onlyFree={onlyFree} />
</div>
)
}Quick question
On a cart page, which value should not be state?
Exercise
This search is case-sensitive, so typing java doesn't find JavaScript. Fix it. The checker types java, and only Web JavaScript 450 should show.
The page should show: Web JavaScript 450
const COURSES = [
{ track: "Web", title: "HTML & CSS", price: 0 },
{ track: "Web", title: "JavaScript", price: 450 },
{ track: "Data", title: "Python", price: 400 },
]
function App() {
const [query, setQuery] = useState("")
const visible = COURSES.filter((c) => c.title.includes(query))
return (
<div>
<input value={query} onChange={(e) => setQuery(e.target.value)} />
<ul>
{visible.map((c) => <li key={c.title}>{c.track} {c.title} {c.price}</li>)}
</ul>
</div>
)
}Learn more from
Played 0 of 2 games
Sign in to save your progress and earn XP.