Aprende a renderizar listas de datos con .map(), la importancia de las keys, y cómo crear componentes dinámicos desde datos
midudev
Más clases y directos en YouTube
Keys y Renderizado de Listas
Hasta ahora hemos trabajado con componentes individuales. En esta clase aprenderemos a renderizar listas de datos dinámicamente usando .map(), y entenderemos por qué React necesita que cada elemento tenga una key única.
El problema: Componentes repetidos manualmente
Imagina que queremos mostrar una lista de trabajos:
function App() { return ( <div> <article className="job-listing-card"> <h3>Frontend Developer</h3> <p>TechCorp</p> <p>Madrid, España</p> </article> <article className="job-listing-card"> <h3>Backend Developer</h3> <p>DataStack</p> <p>Barcelona, España</p> </article> <article className="job-listing-card"> <h3>Full Stack Developer</h3> <p>StartupX</p> <p>Valencia, España</p> </article> </div> )}
Problemas:
- 📝 Repetitivo - Copiar y pegar el mismo HTML
- 🔢 No escala - ¿Y si tienes 100 trabajos?
- 📊 Datos hardcodeados - Los datos están mezclados con la UI
- 🔄 Difícil de actualizar - Cambiar algo requiere tocar múltiples lugares
Creando el archivo de datos
Primero, creemos un archivo JSON con nuestros datos:
// src/data/jobs.json[ { "id": "7a4d1d8b-1e45-4d8c-9f1a-8c2f9a9121a4", "titulo": "Desarrollador de Software Senior", "empresa": "Tech Solutions Inc.", "ubicacion": "Remoto", "descripcion": "Buscamos un ingeniero de software con experiencia en desarrollo web y conocimientos en JavaScript, React y Node.js. El candidato ideal debe ser capaz de trabajar en equipo y tener buenas habilidades de comunicación.", "data": { "technology": ["react", "node", "javascript"], "modalidad": "remoto", "nivel": "senior" } }, { "id": "d35b2c89-5d60-4f26-b19a-6cfb2f1a0f57", "titulo": "Analista de Datos", "empresa": "Data Driven Co.", "ubicacion": "Ciudad de México", "descripcion": "Estamos buscando un analista de datos con experiencia en el manejo de grandes conjuntos de datos y herramientas de visualización. Se requiere conocimiento en SQL, Python y R.", "data": { "technology": "python", "modalidad": "cdmx", "nivel": "junior" } } // ... más elementos]
Creando el componente JobCard
Ahora creemos un componente para mostrar cada trabajo:
// src/components/JobCard.jsxexport function JobCard({ job }) { return ( <article className="job-listing-card" data-modalidad={job.data.modalidad} data-nivel={job.data.nivel} data-technology={job.data.technology} > <div> <h3>{job.titulo}</h3> <small> {job.empresa} | {job.ubicacion} </small> <p>{job.descripcion}</p> </div> <button>Aplicar</button> </article> )}
Puntos clave:
- Recibe
jobcomo prop - Accede a las propiedades del objeto:
job.title,job.company, etc.
Renderizando la lista con .map()
Ahora creemos el componente que renderiza la lista de trabajos:
// src/components/JobListings.jsximport { JobCard } from './JobCard'import jobsData from './data/jobs.json'export function JobListings() { return ( <div className="jobs-grid"> {jobsData.map((job) => ( <JobCard key={job.id} job={job} /> ))} </div> )}
¿Qué hace .map()?
// Array de datosconst jobs = [ { id: 1, title: 'Frontend Developer' }, { id: 2, title: 'Backend Developer' }, { id: 3, title: 'Full Stack Developer' },]// .map() transforma cada elementojobs.map((job) => <JobCard key={job.id} job={job} />)// Resultado: Array de componentes React// [// <JobCard key={1} job={{...}} />,// <JobCard key={2} job={{...}} />,// <JobCard key={3} job={{...}} />// ]
¿Por qué necesitamos la key?
La prop key es obligatoria cuando renderizas listas. Sin ella, verás este warning:
⚠️ Warning: Each child in a list should have a unique "key" prop.
¿Qué es la key?
La key es un identificador único que ayuda a React a identificar qué elementos cambiaron, se agregaron o se eliminaron.
{ jobs.map((job) => <JobCard key={job.id} job={job} />)}// ↑// Obligatoria y única
¿Por qué React necesita keys?
React necesita saber qué elemento es cuál cuando la lista cambia.
Sin keys: React no sabe qué cambió
// Renderizado inicial[ <JobCard />, // Frontend Developer <JobCard />, // Backend Developer <JobCard />, // Full Stack Developer]// Agregas uno al inicio[ <JobCard />, // 🆕 DevOps Engineer <JobCard />, // Frontend Developer <JobCard />, // Backend Developer <JobCard />, // Full Stack Developer]// React piensa: "¿Todos cambiaron? Re-renderizo todos" 🐌
Con keys: React sabe exactamente qué cambió
// Renderizado inicial[ <JobCard key={1} />, // Frontend Developer <JobCard key={2} />, // Backend Developer <JobCard key={3} />, // Full Stack Developer]// Agregas uno al inicio[ <JobCard key={4} />, // 🆕 DevOps Engineer (NUEVO) <JobCard key={1} />, // Frontend Developer (mismo) <JobCard key={2} />, // Backend Developer (mismo) <JobCard key={3} />, // Full Stack Developer (mismo)]// React piensa: "Solo key={4} es nuevo, solo renderizo ese" ⚡
Reglas de las keys
1. Deben ser únicas entre hermanos
// ✅ Correcto: IDs únicos{ jobs.map((job) => <JobCard key={job.id} job={job} />)}// ❌ Error: Todas tienen la misma key{ jobs.map((job) => <JobCard key="job" job={job} />)}
2. Deben ser estables
La key no debería cambiar entre renderizados:
// ❌ Mal: Math.random() genera keys diferentes cada vez{ jobs.map((job) => <JobCard key={Math.random()} job={job} />)}// ✅ Bien: El ID no cambia{ jobs.map((job) => <JobCard key={job.id} job={job} />)}
3. Preferiblemente, usa IDs del dato
// ✅ Mejor: ID del objeto{ jobs.map((job) => <JobCard key={job.id} job={job} />)}// ⚠️ Aceptable solo si la lista NUNCA cambia{ jobs.map((job, index) => <JobCard key={index} job={job} />)}
¿Cuándo usar índices como keys?
Los índices del array (index) solo son seguros cuando:
- ✅ La lista nunca se reordena
- ✅ La lista nunca cambia (no se agregan/eliminan items)
- ✅ Los items no tienen un ID único
Ejemplo donde es seguro:
const DAYS_OF_WEEK = ['Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes']// ✅ OK: Lista estática que nunca cambia{ DAYS_OF_WEEK.map((day, index) => <li key={index}>{day}</li>)}
Ejemplo donde NO es seguro:
const [tasks, setTasks] = useState([...])// ❌ Mal: Lista dinámica que cambia{tasks.map((task, index) => ( <Task key={index} task={task} />))}// ✅ Bien: Usa ID único{tasks.map((task) => ( <Task key={task.id} task={task} />))}
Problema con índices: Ejemplo visual
Imagina una lista de tareas:
// Lista inicial;[ { id: 'a', text: 'Tarea A', completed: false }, { id: 'b', text: 'Tarea B', completed: true }, { id: 'c', text: 'Tarea C', completed: false },]
Con índices como key:
// Renderizado inicial<Task key={0} text="Tarea A" completed={false} /><Task key={1} text="Tarea B" completed={true} /><Task key={2} text="Tarea C" completed={false} />// Usuario elimina "Tarea A"<Task key={0} text="Tarea B" completed={true} /> // ← key cambió!<Task key={1} text="Tarea C" completed={false} /> // ← key cambió!// React piensa: "key={0} cambió de 'Tarea A' a 'Tarea B'"// Puede causar bugs en el estado o animaciones
Con IDs como key:
// Renderizado inicial<Task key="a" text="Tarea A" completed={false} /><Task key="b" text="Tarea B" completed={true} /><Task key="c" text="Tarea C" completed={false} />// Usuario elimina "Tarea A"<Task key="b" text="Tarea B" completed={true} /> // ← key no cambió<Task key="c" text="Tarea C" completed={false} /> // ← key no cambió// React piensa: "key='a' se eliminó, las demás siguen igual"// Todo funciona correctamente
Lista vacía: Renderizado condicional
¿Qué pasa si no hay trabajos? Deberíamos mostrar un mensaje:
export function JobListings({ jobs }) { if (jobs.length === 0) { return ( <div className="no-jobs"> <p>No hay trabajos disponibles en este momento.</p> </div> ) } return ( <div className="jobs-grid"> {jobs.map((job) => ( <JobCard key={job.id} job={job} /> ))} </div> )}
O con operador ternario:
export function JobListings({ jobs }) { return ( <div> {jobs.length === 0 ? ( <p>No hay trabajos disponibles.</p> ) : ( <div className="jobs-grid"> {jobs.map((job) => ( <JobCard key={job.id} job={job} /> ))} </div> )} </div> )}
Lo que hemos visto en esta clase
- 🗂️ Importar JSON - Cargar datos desde archivos externos
- 🧩 Componente JobCard - Mostrar un trabajo individual
- 📋 Componente JobListings - Renderizar lista de trabajos
- 🔄
.map()- Transformar array de datos en array de componentes - 🔑 Key obligatoria - React necesita identificar elementos únicos
- 🎯 Keys únicas y estables - Usar IDs en lugar de índices
- ⚠️ Problema con índices - Por qué no usar
indexen listas dinámicas - 📊 Flujo de datos - JSON → JobListings → JobCard
- 🎭 Renderizado condicional - Mostrar mensaje si lista está vacía
En la próxima clase vamos a terminar de hacer funcionar nuestra paginación.
💡 Recuerda: Siempre usa keys únicas y estables al renderizar listas. Prefiere IDs del objeto sobre índices del array. La key ayuda a React a optimizar los renderizados identificando qué elementos cambiaron.
