Saltar al contenido principal
Logo de JSCamp

Keys y Renderizado de Listas

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

Suscribirse

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 job como 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 index en 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.