Dividir componentes — Props y callbacks de eventos
La componentización es la idea de diseño más central de React. En este capítulo aprenderás a dividir la página en pequeños componentes reutilizables y a implementar la comunicación entre padre e hijo mediante Props y funciones callback.
¿Por qué dividir componentes?
App.jsx se vuelve cada vez más largo, con cientos de líneas de código apiñadas, difíciles de mantener y reutilizar.
Principios para dividir componentes:Cada componente se encarga de una sola cosa; combinados, realizan funciones complejas.。
¿Qué granularidad es adecuada?
| Área de la página | ¿Debería dividirse en componentes? | Razón |
|---|---|---|
| Barra de navegación | Sí | Toda página la necesita; función independiente |
| Tarjeta de artículo | Sí | Cada artículo de la lista tiene la misma estructura |
| Grupo de botones de filtro por categoría | Sí | Tiene su propio estado e interacción |
| Contenedor de envoltura simple | no | Solo para maquetación, sin comportamiento independiente |
De padre a hijo: Props
En React,Props son los parámetros de la función.—Cuando el componente padre llama al componente hijo, le pasa los datos a través de los atributos.
Ejemplo
// Props es el primer parámetro de la función; normalmente se usa con desestructuración
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
El componente padre pasa los datos como si escribiera atributos HTML:
Ejemplo
import BlogCard from './components/BlogCard'
function App() {
const articles = [
{ id: 1, title: 'Introducción a React', summary: 'Aprende React desde cero', date: '2024-05-10', category: 'React' },
{ id: 2, title: 'Explicación detallada de Promise', summary: 'Entendiendo la asincronía', 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 esflujo de datos unidireccional: los datos fluyen del componente padre al hijo. El componente hijo no puede modificar los Props recibidos. Si necesita modificarlos, el componente padre debe manejarlo mediante una función callback.
De hijo a padre: funciones callback
Cuando el componente hijo necesita notificar al padre «ha ocurrido algo», el padre debe pasar unafunción callbackcomo Props.
Ejemplo
function CategoryFilter({ categories, activeCategory, onCategoryChange }) {
return (
<div className="filter-bar">
{categories.map(cat => (
<button
key={cat}
className={activeCategory === cat ? 'active' : ''}
// Al hacer clic, llama a la función callback pasada por el componente padre
onClick={() => onCategoryChange(cat)}
>
{cat}
</button>
))}
</div>
)
}
export default CategoryFilter
El componente padre pasa la función callback:
Ejemplo
import CategoryFilter from './components/CategoryFilter'
function App() {
const [activeCategory, setActiveCategory] = useState('Todos')
const categories = ['Todos', 'React', 'JavaScript', 'CSS']
// Función callback: responde al evento de clic del componente hijo
function handleCategoryChange(cat) {
setActiveCategory(cat)
}
return (
<CategoryFilter
categories={categories}
activeCategory={activeCategory}
onCategoryChange={handleCategoryChange}
/>
)
}
Comparación de comunicación entre React y Vue3
| Escenario | Vue3 | React |
|---|---|---|
| De padre a hijo | Props(defineProps) | Props (parámetros de función) |
| De hijo a padre | Evento emit | Props función callback |
| Dirección de los datos | Unidireccional | Unidireccional |
Vue3 usa el mecanismo emit, React usa funciones callback: en esencia es lo mismo, solo cambia la sintaxis.
Desestructuración de Props y valores predeterminados
Ejemplo
function BlogCard({
title = 'Artículo sin nombre', // Valores predeterminados de desestructuración ES6
summary = 'Sin resumen',
date = '',
category = 'Sin categoría'
}) {
return (
<div className="card">
<span className="tag">{category}</span>
<h3>{title}</h3>
<p>{summary}</p>
{date && <span className="date">{date}</span>}
</div>
)
}
Manos a la obra: dividir en tres componentes
La estructura de componentes del proyecto de blog es la siguiente:
src/ ├── components/ │ ├── NavBar.jsx # 顶部导航栏 │ ├── BlogCard.jsx # 单篇文章卡片 │ └── CategoryFilter.jsx # 分类筛选按钮组 └── App.jsx
NavBar.jsx
Ejemplo
import '../App.css'
function NavBar() {
return (
<header className="navbar">
<a href="/" className="logo">EJEMPLO Blog</a>
<nav>
<a href="/">Inicio</a>
<a href="#">Acerca de</a>
</nav>
</header>
)
}
export default NavBar
App.jsx — Integrando todos los componentes
Ejemplo
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: 'Guía completa de introducción a React', summary: 'Aprende React Hooks desde cero', date: '2024-05-10', category: 'React' },
{ id: 2, title: 'Explicación detallada de programación asíncrona en JS', summary: 'Entiende Promise y async/await', date: '2024-05-08', category: 'JavaScript' },
{ id: 3, title: 'Práctica de diseño con CSS Grid', summary: 'Maquetación responsive con Grid', date: '2024-05-05', category: 'CSS' },
{ id: 4, title: 'React Hooks en profundidad', summary: 'Comprensión profunda de useState y useEffect', date: '2024-05-03', category: 'React' },
{ id: 5, title: 'Guía completa de Flexbox', summary: 'Aprende maquetación flexible en un artículo', date: '2024-05-01', category: 'CSS' },
])
const [activeCategory, setActiveCategory] = useState('Todos')
const categories = useMemo(() => {
const cats = articles.map(a => a.category)
return ['Todos', ...new Set(cats)]
}, [articles])
const filteredArticles = useMemo(() => {
if (activeCategory === 'Todos') return articles
return articles.filter(a => a.category === activeCategory)
}, [articles, activeCategory])
return (
<div className="app">
<NavBar />
<main className="container">
<h2 className="section-title">Artículos recientes</h2>
{/* Componente hijo: recibe props (categories, activeCategory) y callback (onCategoryChange) */}
<CategoryFilter
categories={categories}
activeCategory={activeCategory}
onCategoryChange={setActiveCategory}
/>
<p className="result-info">común{filteredArticles.length}artículo</p>
{filteredArticles.length === 0 ? (
<p className="empty-tip">No hay artículos en esta categoría</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 EJEMPLO Blog. Powered by React.</p>
</footer>
</div>
)
}
export default App
Resumen del capítulo
En este capítulo has dominado la esencia de la comunicación entre componentes de React: de padre a hijo mediante Props (parámetros de función), y de hijo a padre mediante Props como función callback.
Los tres componentes del proyecto de blog, NavBar, BlogCard y CategoryFilter, ya están en su lugar, y la estructura del código es mucho más clara.
Otras extensiones