Los datos de las aplicaciones casi siempre tienen una estructura jerárquica. Tomemos como ejemplo una lista de publicaciones en Códica. Cada publicación tiene un autor y comentarios, que a su vez tienen likes. Estos datos se pueden representar de la siguiente manera:
const posts = [
{
question: '¿Cómo escribir código?',
likesCount: 2,
comments: [
{
answer: '¡Abre el editor!',
likesCount: 1,
createdAt: '11-12-2022',
},
{
answer: '¡Sentado!',
likesCount: 3,
createdAt: '11-12-2022',
},
]
},
{
question: '¿Qué es mejor: vim o emacs?',
likesCount: 2,
comments: [
{
answer: '¡FAR es el mejor!',
likesCount: 100,
createdAt: '11-12-2022',
},
]
}
];
La representación jerárquica de los datos refleja bien su estructura. Podemos ver de inmediato a qué pertenece cada dato, lo cual es conveniente para mostrar y modificar los datos de manera sencilla, especialmente cuando la visualización en pantalla coincide con su estructura y los datos no se superponen entre sí, como ocurre con los temas de Códica. Cada tema tiene su propia entidad independiente (con algunas dependencias, pero que no afectan directamente a los datos).
No obstante, cuando los datos están interrelacionados, la estructura jerárquica puede volverse problemática. Imagina que necesitas mostrar los 10 últimos comentarios. ¿Cómo se haría eso? Tendrías que recorrer todas las publicaciones, extraer todos los comentarios, combinarlos y luego buscar los más recientes. Es un ejemplo que puede resultar complicado.
const comments = posts.flatMap((p) => p.comments);
const sortedComments = comments.sort((c1, c2) => new Date(c2.createdAt) - new Date(c1.createdAt));
const lastComments = sortedComments.slice(0, 10);
La situación empeora cuando hay relaciones muchos a muchos. En ese caso, no está claro dónde colocar a quién. Y siempre habrá situaciones en las que la estructura resultante sea incómoda.
Una forma de salir de esta situación es comenzar a duplicar los datos. Crear estructuras adicionales optimizadas para tareas específicas. Y aunque en general esto tiene sentido, mantener manualmente estas estructuras no es algo bueno. En las bases de datos, por ejemplo, la base de datos misma se encarga de crear índices. Como programadores, no tenemos que preocuparnos por eso. Pero aquí tendríamos que introducir sincronización adicional en todas las etapas: agregar, modificar y eliminar.
Otra forma es normalizar los datos, al igual que en las bases de datos relacionales. Representarlos como matrices planas. Por ejemplo, así:
// En una aplicación real, todo se almacenaría en un solo objeto de estado
const posts = [
{
id: 3,
question: '¿Cómo escribir código?',
likesCount: 2,
},
{
id: 100,
question: '¿Qué es mejor: vim o emacs?',
likesCount: 2,
}
];
const comments = [
{
id: 1,
postId: 3,
answer: '¡Abre el editor!',
likesCount: 1,
},
{
id: 8,
postId: 3,
answer: '¡Sentado!',
likesCount: 3,
},
{
id: 3,
postId: 100,
answer: '¡FAR es el mejor!',
likesCount: 100,
},
]
Si los datos no tienen límites claros y dependen entre sí, esta estructura es mucho más conveniente de trabajar. Permite realizar agregaciones generales y casos especiales de visualización. Además, es importante destacar que los datos normalizados no se duplican. En un estado bien organizado, los datos no se repiten más de una vez.
A veces se aplica la técnica opuesta a la normalización, la desnormalización, pero esto se refiere más al backend que al frontend.
Pero todo tiene un costo. Al simplificar en un lugar, la normalización complica en otro. Ahora, para extraer los comentarios de una publicación específica, tendrías que escribir este código:
const postId = /* identificador de la publicación */;
const commentsForPost = comments.filter((c) => c.postId === postId);
Aquí no hay más código que al seleccionar una publicación específica. Pero es más complejo en términos algorítmicos y requiere más recursos. Si esto es un problema o no, es una pregunta abierta. Por lo general, no lo es. El frontend rara vez opera con grandes cantidades, como decenas o cientos de miles. La mayoría de las veces, el tamaño de las colecciones se limita a cientos de elementos.
En resumen, la mayoría de los mecanismos para almacenar el estado en el frontend recomiendan utilizar el segundo método de almacenamiento. No importa si se hace dentro de un framework o no. Este enfoque es más escalable y funciona bien para cualquier situación. Mientras que el primer enfoque crea muchos problemas cuando la estructura de los datos ya no coincide con su representación.
Para acceder completo a curso necesitas un plan básico
El plan básico te dará acceso completo a todos los cursos, ejercicios y lecciones de Códica, proyectos y acceso de por vida a la teoría de las lecciones completadas. La suscripción se puede cancelar en cualquier momento.