Splitting Components — Props and Event Callbacks
Componentization is the core design philosophy of React. In this chapter, you will learn to split pages into reusable small components and implement parent-child communication through Props and callback functions.
Why split components?
App.jsx keeps getting longer, with hundreds of lines crammed together, making it hard to maintain and reuse.
Principles for splitting components:Each component is responsible for one thing, and they combine to accomplish complex functionality.。
What granularity is appropriate?
| Page area | Whether to split into a component | Reason |
|---|---|---|
| Navigation bar | Yes | Every page needs it; independent functionality |
| Article card | Yes | Every article in the list has the same structure |
| Category filter button group | Yes | Has its own state and interactions |
| Simple wrapper container | no | Only for layout, no independent behavior |
Parent to child: Props
In React,Props are function parameters— when the parent component calls a child component, it passes data in through attributes.
Example
// Props is the first parameter of the function, usually used with destructuring
function BlogCard({ title, summary, date, category }) {
return (
<div className="card">
<span className="tag">{category}</span>
<h3>{title}</h3>
<p>{summary}</p>
<span className="date">{date}</span>
</div>
)
}
export default BlogCard
The parent component passes data in like writing HTML attributes:
Example
import BlogCard from './components/BlogCard'
function App() {
const articles = [
{ id: 1, title: 'React Introduction', summary: 'Learn React from Scratch', date: '2024-05-10', category: 'React' },
{ id: 2, title: 'Promise in Detail', summary: 'Understand Async', date: '2024-05-08', category: 'JavaScript' },
]
return (
<div className="article-grid">
{articles.map(article => (
<BlogCard
key={article.id}
title={article.title}
summary={article.summary}
date={article.date}
category={article.category}
/>
))}
</div>
)
}
Props areunidirectional data flow: data flows from the parent component to the child component. A child component cannot modify the Props it receives. If modification is needed, the parent component should handle it through a callback function.
Child to parent: callback function
When a child component needs to notify the parent component "something happened", the parent component needs to pass acallback functionas Props.
Example
function CategoryFilter({ categories, activeCategory, onCategoryChange }) {
return (
<div className="filter-bar">
{categories.map(cat => (
<button
key={cat}
className={activeCategory === cat ? 'active' : ''}
// Call the callback function passed from the parent component when clicked
onClick={() => onCategoryChange(cat)}
>
{cat}
</button>
))}
</div>
)
}
export default CategoryFilter
The parent component passes the callback function:
Example
import CategoryFilter from './components/CategoryFilter'
function App() {
const [activeCategory, setActiveCategory] = useState('All')
const categories = ['All', 'React', 'JavaScript', 'CSS']
// Callback function: respond to the child component's click event
function handleCategoryChange(cat) {
setActiveCategory(cat)
}
return (
<CategoryFilter
categories={categories}
activeCategory={activeCategory}
onCategoryChange={handleCategoryChange}
/>
)
}
Communication comparison between React and Vue3
| Scenario | Vue3 | React |
|---|---|---|
| Parent to child | Props(defineProps) | Props (function parameters) |
| Child to parent | emit event | Props callback function |
| Data direction | One-way | One-way |
Vue3 uses the emit mechanism, React uses callback functions — the essence is the same, just the syntax differs.
Props destructuring and default values
Example
function BlogCard({
title = 'Untitled Article', // ES6 destructuring default values
summary = 'No Summary Yet',
date = '',
category = 'Uncategorized'
}) {
return (
<div className="card">
<span className="tag">{category}</span>
<h3>{title}</h3>
<p>{summary}</p>
{date && <span className="date">{date}</span>}
</div>
)
}
Hands-on: split out three components
The component structure of the blog project is as follows:
src/ ├── components/ │ ├── NavBar.jsx # 顶部导航栏 │ ├── BlogCard.jsx # 单篇文章卡片 │ └── CategoryFilter.jsx # 分类筛选按钮组 └── App.jsx
NavBar.jsx
Example
import '../App.css'
function NavBar() {
return (
<header className="navbar">
<a href="/" className="logo">EXAMPLE Blog</a>
<nav>
<a href="/">Home</a>
<a href="#">About</a>
</nav>
</header>
)
}
export default NavBar
App.jsx — Integrating all components
Example
import { useState, useMemo } from 'react'
import NavBar from './components/NavBar'
import BlogCard from './components/BlogCard'
import CategoryFilter from './components/CategoryFilter'
import './App.css'
function App() {
const [articles] = useState([
{ id: 1, title: 'The Complete Guide to Getting Started with React', summary: 'Learning React Hooks from Scratch', date: '2024-05-10', category: 'React' },
{ id: 2, title: 'JS Asynchronous Programming In Detail', summary: Understand Promise and async/await, date: '2024-05-08', category: 'JavaScript' },
{ id: 3, title: Practical CSS Grid Layout, summary: 'Implement Responsive Layout with Grid', date: '2024-05-05', category: 'CSS' },
{ id: 4, title: 'React Hooks Deep Dive', summary: 'Deep understanding of useState and useEffect', date: '2024-05-03', category: 'React' },
{ id: 5, title: 'The Complete Guide to Flexbox', summary: 'Learn Flex Layout in One Article', date: '2024-05-01', category: 'CSS' },
])
const [activeCategory, setActiveCategory] = useState('All')
const categories = useMemo(() => {
const cats = articles.map(a => a.category)
return ['All', ...new Set(cats)]
}, [articles])
const filteredArticles = useMemo(() => {
if (activeCategory === 'All') return articles
return articles.filter(a => a.category === activeCategory)
}, [articles, activeCategory])
return (
<div className="app">
<NavBar />
<main className="container">
<h2 className="section-title">Latest articles</h2>
{/* Child component: receives props (categories, activeCategory) and callback (onCategoryChange) */}
<CategoryFilter
categories={categories}
activeCategory={activeCategory}
onCategoryChange={setActiveCategory}
/>
<p className="result-info">common{filteredArticles.length}article</p>
{filteredArticles.length === 0 ? (
<p className="empty-tip">No articles under this category yet</p>
) : (
<div className="article-grid">
{filteredArticles.map(article => (
<BlogCard
key={article.id}
title={article.title}
summary={article.summary}
date={article.date}
category={article.category}
/>
))}
</div>
)}
</main>
<footer className="footer">
<p>© 2024 EXAMPLE Blog. Powered by React.</p>
</footer>
</div>
)
}
export default App
Chapter summary
In this chapter, you mastered the core of React component communication: parent to child via Props (function parameters), child to parent via Props callback functions.
The three components of the blog project — NavBar, BlogCard, and CategoryFilter — are in place, and the code structure is much clearer.
Other extensions