División detallada de componentes y cuestiones de propiedad estatal
Después de dividir un estado en múltiples verdades locales, la secuencia se convierte en un evento probabilístico.
Los síntomas de este error en línea son muy similares a los “ocasionales”, pero no son aleatorios.
En la misma página, un estado comercial existirá en diferentes formas en diferentes componentes: parámetros de URL, estado del componente principal, estado local del componente secundario, caché devuelto por la solicitud e incluso valores derivados calculados por un determinado selector. Cuanto más finos se descomponen los componentes, más se vuelven estas “verdades parciales”. Mientras “quién puede escribir, quién sabe y quién es responsable del tiempo” no converja primero en una regla, la causa raíz del estado de error en línea cambiará de “un determinado fragmento de código está escrito incorrectamente” a “varios fragmentos de código están escritos correctamente, pero el orden de escritura es inestable”.
Lo más difícil de este tipo de problemas es solucionarlos, no repararlos. Porque parece que cada componente es muy razonable: mantienen su propio pequeño estado, almacenamiento en caché y carga. Pero cuando se combinan, el sistema no tiene un propietario de estado único y el rendimiento final es: simplemente actualizar, cambiar de pestaña e intentarlo de nuevo. Si desea utilizar registros para conectar enlaces, encontrará que el mismo campo proviene de accesorios, caché local y paquetes de devolución de solicitudes. Quién cubre a quién depende enteramente del ritmo de presentación y del retraso de la solicitud.
El juicio de este artículo es simple:
Si la división de componentes no converge primero en “quién escribe el estado, quién conoce los detalles y quién es responsable del tiempo”, el mismo estado se dividirá en múltiples verdades parciales y la secuencia de actualización se convertirá en un evento probabilístico. Finalmente, los beneficios de la reutilización serán reemplazados por estados de error ocasionales, y se producirá el costo de la renderización repetida y la resolución de problemas.
A continuación, utilizaré una solución de problemas de estado de error en línea muy típica para explicar cómo converger paso a paso.
Escena: Un estado de error “ocasional”
La página es una lista + barra de filtro superior.
- Hay consulta:
?tab=all&sort=latest&city=shen la URL - La barra de filtro superior está dividida en varios componentes pequeños: pestaña, orden, ciudad desplegable
- El componente de lista almacena en caché el “resultado de la última solicitud” para evitar parpadeos al cambiar de filtro.
Los comentarios de los usuarios son: Al cambiar rápidamente de pestaña y ordenar, a veces la lista mostrará “elementos de filtro del nuevo orden”, pero los datos de la lista siguen siendo “los resultados del antiguo orden”. Hacer clic en el mismo Ordenar nuevamente funciona bien.
A primera vista, parece que la interfaz es inconsistente, pero la captura de paquetes muestra que la interfaz no devuelve ningún problema y el eco sort en el cuerpo devuelto también es correcto. En otras palabras, el servidor tiene razón y lo que está mal es el “estado mostrado” en la interfaz.
Primer error de juicio: pensé que era una condición de carrera solicitada
La intuición le hará sospechar: la solicitud de A es lenta, la solicitud de B es rápida, B regresa primero y genera el resultado correcto, y luego A regresa y sobrescribe el resultado anterior.
Este tipo de condición de carrera es realmente común, por lo que primero agregamos el requestId y descartamos el paquete devuelto: solo se acepta la última solicitud enviada.
Después de conectarme, el problema disminuyó un poco, pero no desapareció. Explique que la “cobertura del paquete de devolución” no es el único canal.
El valor de este paso es: primero corta una parte de un espacio problemático aparentemente grande. Ahora es seguro que al menos algunos de los estados defectuosos no son causados por el orden de la red.
El segundo error de juicio: pensé que era un error en la lógica del caché.
Luego veamos el caché del componente de lista.
La estrategia de almacenamiento en caché es:
- Pase
filtersdesde accesorios - El componente de lista utiliza internamente
useRefpara guardarlastGoodData. - Activar la solicitud si
filterscambia - Continúe mostrando
lastGoodDatadurante la solicitud y luego reemplácelo cuando vuelvan nuevos datos.
No hay nada de malo en esta lógica de “reducir el parpadeo”, pero oculta una premisa: filters debe ser una fuente única y estable de verdad. De lo contrario, es fácil que aparezca: filters ha cambiado, pero la lista todavía usa el antiguo lastGoodData, y en la superficie se pensará que es solo un encubrimiento durante la carga.
Pensé que era porque la referencia del objeto filters era inestable, lo que provocaba que el tiempo de activación del efecto fuera confuso. Se cambió a clave de serialización explícita: filtersKey = tab + sort + city.
Todavía no hay cura.
La verdadera causa fundamental: la propiedad estatal está destrozada
Después de escribir finalmente todos los registros, el problema quedó claro:
- El componente Tab solo se preocupa por
tab: - Al hacer clic,
setLocalTab(nextTab)se resaltará inmediatamente - Entonces
onChange(nextTab)notifica al componente principal. - Vaya a
setFilters({ ... })después de recibir el componente principal. - El componente de clasificación también tiene el mismo patrón.
- Para admitir la “actualización reanudable”, el componente principal:
- Primero extraiga los filtros iniciales del análisis de URL.
- Luego escríbalo al estado
- Los componentes de la lista también reciben:
- Accesorios
filtersdel componente principal. - y un
getCachedResult(filtersKey)del módulo de caché
En otras palabras, un estado tiene al menos tres conjuntos de fuentes:
- Estado local del subcomponente: utilizado para interacción de retroalimentación inmediata
- El estado del componente principal: como filtros a nivel de página
- Módulo de caché: utilizado como backend de visualización de datos
No existe un contrato estricto de “orden de escritura” entre ellos.
El enlace donde se produce el problema suele ser el siguiente:
- Ordenar puntos de usuario
- El componente Ordenar actualiza inmediatamente su estado local y la interfaz de usuario muestra “Nueva clasificación seleccionada”.
- El
filtersdel componente principal no ha tenido tiempo de actualizarse (o se actualizó pero no se transmitirá hasta el siguiente fotograma) - El componente de lista volverá a calcular
filtersKeyen este momento - Pero no cuenta los nuevos filtros del componente padre.
- En cambio, una ruta derivada se mezcla con el valor local de Ordenar (como a través del contexto o selector)
filtersKeycambió, por lo que la lista fue al módulo de caché y obtuvo un resultado antiguo que “parecía coincidir”.- Cuando la solicitud regresa, debido a la política de descarte de requestId, siempre que no sea la última vez, se descartará.
- La interfaz de usuario final tiene una combinación extraña: la barra de filtro es el nuevo valor y los datos de la lista provienen del caché anterior.
Se trata de “varios fragmentos de código están escritos correctamente, pero el orden es inestable”.
La división de componentes corta los derechos de escritura del estado en pedazos: el componente secundario escribe primero una copia para obtener una respuesta instantánea de la interacción, el componente principal escribe otra copia para su reproducción y el caché escribe otra copia por el bien de la experiencia. No hay nada de malo en ello, pero el sistema carece de una propiedad estatal unificada.
Cómo dejar de hablar: primero decida la propiedad y luego hable sobre la reutilización
La solución a este tipo de problema es plasmar los derechos de escritura, las reglas de derivación y las estrategias de encubrimiento del Estado en un contrato ejecutable.
Terminé decidiéndome por tres reglas.
Regla 1: los filtros de página solo tienen una fuente de escritura
Los componentes secundarios ya no mantienen su propio estado de filtros locales.
El estado del componente principal proporciona retroalimentación interactiva inmediata, y el componente secundario solo es responsable de enviar eventos y no de almacenar valores. Es decir:
- Subconjunto:
onSelect(next) - Componente principal:
setFilters(reduce(prev, action)) - Visualización de subcomponentes:
value={filters.sort}de solo lectura
El costo de esto es: el subcomponente se volverá “tonto” y será necesario pasar más accesorios cuando se reutilice. Pero lo que intercambia es una determinada vía de escritura.
Regla 2: los valores derivados deben indicar explícitamente la fuente
Todas las claves utilizadas para solicitudes y almacenamiento en caché solo pueden generarse a partir de filters.
Está prohibido generar otro conjunto de claves directamente desde el contexto, selector o URL.
Esto puede parecer una mística, pero es para evitar “campos con el mismo nombre provenientes de diferentes fuentes”. Una vez que se permita que sort provenga tanto del estado local como del estado principal, encontrará “un conjunto de UI y otro conjunto de datos” de hoy.
Regla 3: el caché solo muestra los detalles y no participa en el juicio de estado.
El módulo de caché solo proporciona una capacidad: getLastGoodData(filtersKey).
No puede determinar cuál es la clave de filtro actual, y mucho menos usar los datos de una clave antigua como “resultado actual”.
Los pasos específicos son:
- El estado de la solicitud de lista es claro:
currentFiltersKeyse pasa desde el componente principal. - Permitido al mostrar:
data = loading ? cache[currentFiltersKey] ?? null : result- pero el almacenamiento en caché nunca afecta a los filtros
Esto degrada el caché de “participar en el estado del sistema” a “mostrar únicamente el resultado final”. Sacrificará un poco de experiencia, por ejemplo, algunos interruptores estarán vacíos por un tiempo, pero devolverá certeza.
Contraejemplo: “Promover todos los estados al componente principal” también fallará
Después de escuchar esto, algunas personas dirán: simplemente lleven todos los estados al nivel más alto.
La versión que he visto fallar es: la capa superior se convierte en la única fuente de verdad, pero también contiene:
- Sincronización de URL
- Persistencia local
- Solicitar limitación
- golpe de caché
- Estado interactivo de la interfaz de usuario (desplazar el cursor, enfocar, panel abierto)
Como resultado, el reductor de nivel superior se convierte en un núcleo de negocio, y cualquier pequeña interacción tiene que pasar por un montón de lógica, y el costo de la modificación se dispara. Al final, todos comenzaron a evitarlo y agregaron secretamente el estado local a los subcomponentes, y el sistema volvió a “múltiples verdades locales”.
Así que la clave es “aclarar qué estados requieren propiedad exclusiva”.
Generalmente lo juzgo en una oración: siempre que afecte las solicitudes, las claves de caché o la coherencia entre componentes, debe ser de propiedad exclusiva. Los transitorios puros de la interfaz de usuario se pueden localizar.
Límite aplicable: no todas las divisiones de componentes causarán dificultades
Esta crítica no se refiere a la división de componentes per se.
Originalmente, la división estaba destinada a controlar la complejidad, pero tiene un costo implícito: el “límite de la escritura estatal” debe diseñarse adicionalmente.
Cuando la página cumpla alguna de las siguientes condiciones, esta trampa aparecerá fácilmente:
- Tiene URL/recuperación persistente
- Hay solicitudes o cancelaciones concurrentes.
- Hay una pantalla en caché
- Tener filtros compartidos entre componentes.
Por otro lado, si la página es muy simple, el estado no afecta la solicitud y no hay almacenamiento en caché ni recuperación, el estado local no se convertirá en un desastre.
Resumen
Descomponer componentes en pedazos pequeños no traerá automáticamente beneficios de reutilización; primero creará problemas de propiedad estatal.
Si no responde primero “¿Quién puede escribir, quién conoce los detalles y quién es responsable del tiempo?”, el sistema responderá por sí solo: quien rinda primero tendrá la última palabra y quien regrese tarde la cubrirá.
Cuando ocurren estados de error “ocasionales” en la línea, encontrará que lo que es realmente costoso es recuperar un estado a partir de múltiples verdades parciales en un determinado enlace de escritura. \
What to read next
Want more posts about Uncategorized?
Posts in the same category are usually the best next step for reading more on this topic.
View same categoryWant to explore another direction?
If you are not sure what to read next, return to the homepage and start from categories, topics, or latest updates.
Back home