He estado reflexionando sobre la mejor manera de manejar la agrupación en mi aplicación. Es una aplicación de edición de video y estoy introduciendo la capacidad de agrupar capas. Si está familiarizado con Figma o cualquier programa de diseño/edición de video, generalmente existe la posibilidad de agrupar capas.
Para mantener esto simple en la aplicación, los datos de video son un mapa
const map = { "123": { uid: "123", top: 25, type: "text" }, "345": { uid: "345", top: 5, type: "image" }, "567": { uid: "567", top: 25, type: "group" children: ["345", "123"] } }Luego los estoy agrupando dentro de una función de renderizado (esto parece costoso)
const SomeComponent = () => { const objects = useMemo(() => makeTrackObjects(map), [map]); return ( <div> {objects.map(object => { return <div>Some layer that will change the data causing re-renders</div> })} </div> ) }Aquí está la función que hace la agrupación.
const makeTrackObjects = (map) => { // converts map to array const objects = Object.keys(map).map((key: string) => ({ ...map[key] })); // flat array of all objects to be grouped by their key/id const objectsInGroup = objects .filter((object) => object.type === "group") .map((object) => object.children) .flat(); // filter out objects that are nested/grouped const filtered = objects.filter((object) => !objectsInGroup.includes(object.uid)) // insert objects as children during render const grouped = filtered.map((object) => { const children = object.children ? { children: object.children .map((o, i) => { return { ...map[o] }; }) .flat() } : {}; return { ...object, ...children }; }); // the core data is flat but now nested for the UI. Is this inefficient? return grouped }Idealmente, me gustaría mantener los datos planos, tengo mucho código que tendría que actualizar para profundizar en los datos. Se siente bien tenerlo plano y transformadores en ciertas áreas donde se necesitan.
La pregunta principal es si esto tiene sentido, si es eficiente y, si no, ¿por qué?
Si tiene problemas de rendimiento, un área que puede querer investigar es cómo está encadenando funciones de matriz ( map , filter , flat , etc.). Cada llamada a una de estas funciones crea una colección intermedia basada en la matriz que recibe. (Por ejemplo, si encadenamos 2 funciones de map , esto es recorrer la matriz completa dos veces). Puede aumentar el rendimiento creando un bucle y agregando elementos a una colección. (Aquí hay un artículo que menciona que esto es una motivación para los transductores).
No he encontrado un problema de rendimiento con esto antes, pero es posible que también desee eliminar la propagación ( ... ) cuando no sea necesario.
Esta es mi opinión sobre esos ajustes en makeTrackObjects .
Actualizar
También noté que está utilizando include mientras itera a través de una matriz. Esto es efectivamente una complejidad de tiempo O(n^2) porque cada elemento se escaneará contra la matriz completa. Una forma de mitigar es usar un Set para verificar si ese contenido ya existe, convirtiendo esto en una complejidad de tiempo O(n) .
const map = { "123": { uid: "123", top: 25, type: "text" }, "345": { uid: "345", top: 5, type: "image" }, "567": { uid: "567", top: 25, type: "group", children: ["345", "123"] } }; const makeTrackObjects = (map) => { // converts map to array const objects = Object.keys(map).map((key) => map[key]); // set of all objects to be grouped by their key/id const objectsInGroup = new Set(); objects.forEach(object => { if (object.type === "group") { object.children.forEach(child => objectsInGroup.add(child)); } }); // filter out objects that are nested/grouped const filtered = objects.filter((object) => !objectsInGroup.has(object.uid)) // insert objects as children during render const grouped = filtered.map((object) => { const children = {}; if (object.children) { children.children = object.children.map(child => map[child]); } return { ...object, ...children }; }); // the core data is flat but now nested for the UI. Is this inefficient? return grouped } console.log(makeTrackObjects(map));