fix: checkbox de tarea (hecho) no se actualizaba tras guardar
useDragReorder resincronizaba su copia local solo cuando cambiaba el conjunto de ids, no cuando se editaba un campo de una tarea existente (marcar hecho, renombrar) -- la fila se quedaba mostrando el snapshot viejo para siempre despues de guardar. Ahora compara el contenido completo. De paso: draggable/onDragStart pasan a vivir solo en el icono de agarre, nunca en toda la fila -- un ancestro draggable=true le gana el mousedown a los controles interactivos que tenga adentro en algunos navegadores (no era la causa de este bug puntual, pero es un problema real del mismo patron que conviene cerrar ya que se reutiliza en cualquier lista futura). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,15 +5,24 @@ import { useEffect, useRef, useState } from 'react';
|
||||
// mientras se arrastra (estado local optimista); el llamador persiste el orden final.
|
||||
// Reutilizado por ListView (actividades, y cualquier listado que pase onReorder) y por el listado
|
||||
// de tareas de una actividad.
|
||||
//
|
||||
// `getHandleProps` (draggable + onDragStart/onDragEnd) va SOLO en el icono de arrastre, nunca en
|
||||
// la fila completa: un ancestro `draggable=true` le gana el mousedown a los controles interactivos
|
||||
// que tenga adentro (checkboxes, botones) y el navegador deja de dispararles click/change. Los que
|
||||
// llaman a este hook siempre deben combinar `getHandleProps` en el handle con `getRowProps`
|
||||
// (onDragOver/onDrop, para poder soltar en cualquier punto de la fila) en la fila.
|
||||
export function useDragReorder(items, onReorder, getId = (x) => x.id) {
|
||||
const [order, setOrder] = useState(items);
|
||||
const orderRef = useRef(items);
|
||||
const draggingId = useRef(null);
|
||||
|
||||
// Se sincroniza por el CONTENIDO (ids), no por la referencia del arreglo: un llamador que pase
|
||||
// `algo || []` (comun) crea un arreglo nuevo en cada render aunque el contenido no cambie, y
|
||||
// sincronizar por referencia ahi entra en loop infinito de renders.
|
||||
const itemsKey = items.map(getId).join(',');
|
||||
// Se sincroniza por el CONTENIDO completo (JSON), no por la referencia del arreglo ni solo por
|
||||
// los ids: un llamador que pase `algo || []` (comun) crea un arreglo nuevo en cada render aunque
|
||||
// el contenido no cambie (sincronizar por referencia ahi entra en loop infinito de renders), pero
|
||||
// un key basado solo en ids tampoco alcanza — no cambia cuando se edita un campo de un item ya
|
||||
// existente (ej. marcar una tarea como hecha, renombrarla), y el checkbox/nombre se queda
|
||||
// mostrando el snapshot viejo para siempre tras guardar.
|
||||
const itemsKey = JSON.stringify(items);
|
||||
useEffect(() => {
|
||||
if (!draggingId.current) { setOrder(items); orderRef.current = items; }
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@@ -21,11 +30,21 @@ export function useDragReorder(items, onReorder, getId = (x) => x.id) {
|
||||
|
||||
useEffect(() => { orderRef.current = order; }, [order]);
|
||||
|
||||
function getDragHandlers(item) {
|
||||
function getHandleProps(item) {
|
||||
const itemId = getId(item);
|
||||
return {
|
||||
draggable: true,
|
||||
onDragStart: (e) => { draggingId.current = itemId; e.dataTransfer.effectAllowed = 'move'; },
|
||||
onDragEnd: () => {
|
||||
draggingId.current = null;
|
||||
onReorder(orderRef.current.map(getId));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function getRowProps(item) {
|
||||
const itemId = getId(item);
|
||||
return {
|
||||
onDragOver: (e) => {
|
||||
e.preventDefault();
|
||||
if (draggingId.current === null || draggingId.current === itemId) return;
|
||||
@@ -40,12 +59,8 @@ export function useDragReorder(items, onReorder, getId = (x) => x.id) {
|
||||
});
|
||||
},
|
||||
onDrop: (e) => e.preventDefault(),
|
||||
onDragEnd: () => {
|
||||
draggingId.current = null;
|
||||
onReorder(orderRef.current.map(getId));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return { order, getDragHandlers, isDragging: (itemId) => draggingId.current === itemId };
|
||||
return { order, getHandleProps, getRowProps, isDragging: (itemId) => draggingId.current === itemId };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user