Creando tablas ordenables con React
Hacer que sus tablas se puedan ordenar en React puede parecer una tarea desalentadora, pero no tiene por qué ser demasiado difícil. En este artículo, implementaremos todo lo que necesita para resolver todas sus necesidades de clasificación de tablas.
La clasificación de tablas siempre ha sido una cuestión bastante difícil de lograr. Hay muchas interacciones que realizar un seguimiento, extensas mutaciones DOM que realizar e incluso algoritmos de clasificación intrincados. Es sólo uno de esos desafíos que son difíciles de resolver. ¿Bien?
En lugar de recurrir a bibliotecas externas, intentemos crear cosas nosotros mismos. En este artículo, crearemos una forma reutilizable de ordenar sus datos tabulares en React. Revisaremos cada paso en detalle y aprenderemos un montón de técnicas útiles a lo largo del camino.
No analizaremos la sintaxis básica de React o JavaScript, pero no es necesario ser un experto en React para seguirlo.
Creando una tabla con reaccionar
Primero, creemos un componente de tabla de muestra. Aceptará una variedad de productos y generará una tabla muy básica, enumerando una fila por producto.
function ProductTable(props) { const { products } = props; return ( table captionOur products/caption thead tr thName/th thPrice/th thIn Stock/th /tr /thead tbody {products.map(product = ( tr key={product.id} td{product.name}/td td{product.price}/td td{product.stock}/td /tr ))} /tbody /table );}
Aquí, aceptamos una variedad de productos y los colocamos en nuestra tabla. Es estático y no se puede ordenar en este momento, pero está bien por ahora.
Ordenar los datos
Si les creyeras a todos los entrevistadores de pizarra, pensarías que el desarrollo de software consistía casi exclusivamente en algoritmos de clasificación. Afortunadamente, aquí no analizaremos una clasificación rápida o una clasificación de burbujas.
Ordenar datos en JavaScript es bastante sencillo, gracias a la función de matriz incorporada sort()
. Ordenará matrices de números y cadenas sin un argumento adicional:
const array = ['mozzarella', 'gouda', 'cheddar'];array.sort();console.log(array); // ['cheddar', 'gouda', 'mozzarella']
Si quieres algo un poco más inteligente, puedes pasarle una función de clasificación. Esta función recibe dos elementos de la lista como argumentos y colocará uno delante del otro según lo que usted decida.
Comencemos ordenando los datos que obtenemos alfabéticamente por nombre.
function ProductTable(props) { const { products } = props; let sortedProducts = [...products]; sortedProducts.sort((a, b) = { if (a.name b.name) { return -1; } if (a.name b.name) { return 1; } return 0; }); return ( Table {/* as before */} /Table );}
Entonces, ¿qué está pasando aquí? Primero, creamos una copia de la propiedad del producto, que podemos alterar y cambiar como queramos. Necesitamos hacer esto porque la Array.prototype.sort
función altera la matriz original en lugar de devolver una nueva copia ordenada.
A continuación, llamamos sortedProducts.sort
y le pasamos una sorting
función. Comprobamos si la name
propiedad del primer argumento a
está antes del segundo argumento b
y, de ser así, devolvemos un valor negativo. Esto indica que a
debería aparecer antes b
en la lista. Si el nombre del primer argumento está después del nombre del segundo argumento, devolvemos un número positivo, lo que indica que debemos colocarlo b
antes a
. Si los dos son iguales (es decir, ambos tienen el mismo nombre), volvemos 0
a preservar el orden.
Hacer que nuestra mesa se pueda ordenar
Ahora podemos asegurarnos de que la tabla esté ordenada por nombre, pero ¿cómo podemos cambiar el orden de clasificación nosotros mismos?
Para cambiar el campo por el que ordenamos, debemos recordar el campo ordenado actualmente. Lo haremos con el useState
gancho.
Un gancho es un tipo especial de función que nos permite "engancharnos" a algunas de las funciones principales de React, como gestionar el estado y activar efectos secundarios. Este gancho en particular nos permite mantener una parte del estado interno de nuestro componente y cambiarlo si queremos. Esto es lo que agregaremos:
const [sortedField, setSortedField] = React.useState(null);
Empezamos por no clasificar nada en absoluto. A continuación, modifiquemos los encabezados de la tabla para incluir una forma de cambiar el campo por el que queremos ordenar.
const ProductsTable = (props) = { const { products } = props; const [sortedField, setSortedField] = React.useState(null); return ( table thead tr th button type="button" onClick={() = setSortedField('name')} Name /button /th th button type="button" onClick={() = setSortedField('price')} Price /button /th th button type="button" onClick={() = setSortedField('stock')} In Stock /button /th /tr /thead {/* As before */} /table );};
Ahora, cada vez que hacemos clic en el encabezado de una tabla, actualizamos el campo por el que queremos ordenar. ¡Genial!
Sin embargo, todavía no estamos haciendo ninguna clasificación, así que arreglémoslo. ¿Recuerdas el algoritmo de clasificación de antes? Aquí está, ligeramente modificado para que funcione con cualquiera de nuestros nombres de campo.
const ProductsTable = (props) = { const { products } = props; const [sortedField, setSortedField] = React.useState(null); let sortedProducts = [...products]; if (sortedField !== null) { sortedProducts.sort((a, b) = { if (a[sortedField] b[sortedField]) { return -1; } if (a[sortedField] b[sortedField]) { return 1; } return 0; }); } return ( table
Primero nos aseguramos de haber elegido un campo para ordenar y, de ser así, ordenamos los productos por ese campo.
Ascendente vs Descendente
La siguiente característica que queremos ver es una forma de cambiar entre orden ascendente y descendente. Cambiaremos entre orden ascendente y descendente haciendo clic en el encabezado de la tabla una vez más.
Para implementar esto, necesitaremos introducir una segunda parte del estado: el orden de clasificación. Refactorizaremos nuestra sortedField
variable de estado actual para mantener tanto el nombre del campo como su dirección. En lugar de contener una cadena, esta variable de estado contendrá un objeto con una clave (el nombre del campo) y una dirección. Le cambiaremos el nombre para sortConfig
que quede un poco más claro.
Aquí está la nueva función de clasificación:
sortedProducts.sort((a, b) = { if (a[sortConfig.key] b[sortConfig.key]) { return sortConfig.direction === 'ascending' ? -1 : 1; } if (a[sortConfig.key] b[sortConfig.key]) { return sortConfig.direction === 'ascending' ? 1 : -1; } return 0;});
Ahora bien, si la dirección es 'ascendente', haremos lo mismo que hicimos anteriormente. Si no es así, haremos lo contrario, dándonos un orden descendente.
A continuación, crearemos una nueva función requestSort
que aceptará el nombre del campo y actualizará el estado en consecuencia.
const requestSort = key = { let direction = 'ascending'; if (sortConfig.key === key sortConfig.direction === 'ascending') { direction = 'descending'; } setSortConfig({ key, direction });}
¡También tendremos que cambiar nuestros controladores de clics para usar esta nueva función!
return ( table thead tr th button type="button" onClick={() = requestSort('name')} Name /button /th th button type="button" onClick={() = requestSort('price')} Price /button /th th button type="button" onClick={() = requestSort('stock')} In Stock /button /th /tr /thead {/* as before */} /table);
Ahora estamos empezando a vernos con todas las funciones, pero aún queda una gran cosa por hacer. Necesitamos asegurarnos de que solo ordenamos nuestros datos cuando sea necesario. Actualmente, estamos clasificando todos nuestros datos en cada renderizado, lo que generará todo tipo de problemas de rendimiento en el futuro. En su lugar, ¡utilicemos el gancho incorporado useMemo
para memorizar todas las partes lentas!
const ProductsTable = (props) = { const { products } = props; const [sortConfig, setSortConfig] = React.useState(null); React.useMemo(() = { let sortedProducts = [...products]; if (sortedField !== null) { sortedProducts.sort((a, b) = { if (a[sortConfig.key] b[sortConfig.key]) { return sortConfig.direction === 'ascending' ? -1 : 1; } if (a[sortConfig.key] b[sortConfig.key]) { return sortConfig.direction === 'ascending' ? 1 : -1; } return 0; }); } return sortedProducts; }, [products, sortConfig]);
Si no lo ha visto antes, useMemo
es una forma de almacenar en caché (o memorizar) cálculos costosos. Entonces, dada la misma entrada, no es necesario ordenar los productos dos veces si volvemos a renderizar nuestro componente por algún motivo. Tenga en cuenta que queremos activar una nueva clasificación cada vez que nuestros productos cambian, o el campo o la dirección por la que clasificamos cambia.
¡Envolver nuestro código en esta función tendrá enormes implicaciones de rendimiento para nuestra clasificación de tablas!
Hacerlo todo reutilizable
Una de las mejores cosas de los ganchos es lo fácil que es hacer que la lógica sea reutilizable. Probablemente estará ordenando todo tipo de tablas a lo largo de su aplicación, y tener que volver a implementar las mismas cosas nuevamente suena como una molestia.
React tiene esta característica llamada ganchos personalizados . Suenan elegantes, pero lo único que son son funciones regulares que utilizan otros ganchos dentro de ellas. ¡Refactoricemos nuestro código para que esté contenido en un gancho personalizado, para que podamos usarlo en todas partes!
const useSortableData = (items, config = null) = { const [sortConfig, setSortConfig] = React.useState(config); const sortedItems = React.useMemo(() = { let sortableItems = [...items]; if (sortConfig !== null) { sortableItems.sort((a, b) = { if (a[sortConfig.key] b[sortConfig.key]) { return sortConfig.direction === 'ascending' ? -1 : 1; } if (a[sortConfig.key] b[sortConfig.key]) { return sortConfig.direction === 'ascending' ? 1 : -1; } return 0; }); } return sortableItems; }, [items, sortConfig]); const requestSort = key = { let direction = 'ascending'; if (sortConfig sortConfig.key === key sortConfig.direction === 'ascending') { direction = 'descending'; } setSortConfig({ key, direction }); } return { items: sortedItems, requestSort };}
Esto es básicamente copiar y pegar de nuestro código anterior, con un poco de cambio de nombre. useSortableData
Acepta los elementos y un estado de clasificación inicial opcional. Devuelve un objeto con los elementos ordenados y una función para reordenar los elementos.
El código de nuestra tabla ahora se ve así:
const ProductsTable = (props) = { const { products } = props; const { items, requestSort } = useSortableData(products); return ( table{/* ... */}/table );};
Un último toque
Falta una pequeña pieza: una forma de indicar cómo está ordenada la mesa. Para indicar eso en nuestro diseño, también necesitamos devolver el estado interno: el sortConfig
. ¡Devolvámoslo también y usémoslo para generar estilos que podamos aplicar a los encabezados de nuestras tablas!
const ProductTable = (props) = { const { items, requestSort, sortConfig } = useSortableData(props.products); const getClassNamesFor = (name) = { if (!sortConfig) { return; } return sortConfig.key === name ? sortConfig.direction : undefined; }; return ( table captionProducts/caption thead tr th button type="button" onClick={() = requestSort('name')} className={getClassNamesFor('name')} Name /button /th {/* … */} /tr /thead {/* … */} /table );};
¡Y con eso, terminamos!
Terminando
Resulta que crear su propio algoritmo de clasificación de tablas no fue una tarea imposible después de todo. Encontramos una manera de modelar nuestro estado, escribimos una función de clasificación genérica y escribimos una manera de actualizar cuáles son nuestras preferencias de clasificación. Nos aseguramos de que todo funcionara y lo refactorizamos todo en un gancho personalizado. Finalmente, proporcionamos una forma de indicar el orden de clasificación al usuario.
Puedes ver una demostración de la tabla en este CodeSandbox :
(ra, yk, il)Explora más en
- Reaccionar
- javascript
- Herramientas
- Técnicas
Deja un comentario