Hola Habr!
Con increíble orgullo y alivio, entregamos un
nuevo libro sobre React a la imprenta esta noche.

En esta ocasión, le ofrecemos una traducción ligeramente resumida de un artículo de Dan Abramov, que describe el uso de interceptores en la versión 16 de React. El libro, que ya estamos esperando, se describe en el Capítulo 5.
La semana pasada,
Sophie Alpert y yo presentamos el concepto de "interceptores" en la conferencia React Conf, seguido de una
discusión detallada del
tema de
Ryan Florence .
Le recomiendo encarecidamente que vea esta conferencia plenaria para familiarizarse con la variedad de problemas que estamos tratando de resolver con la ayuda de interceptores. Sin embargo, incluso la hora de su tiempo lo valoro mucho, así que decidí resumir brevemente en este artículo las principales consideraciones para los interceptores.
Nota: los interceptores React todavía son experimentales. No es necesario profundizar en ellos en este momento. También tenga en cuenta que esta publicación establece mis puntos de vista personales, que pueden no coincidir con la posición de los desarrolladores de React.
¿Por qué se necesitan interceptores?Se sabe que la organización de componentes y un flujo descendente de datos ayudan a organizar una gran interfaz de usuario en forma de fragmentos pequeños, independientes y reutilizables.
Sin embargo, a menudo no es posible dividir componentes complejos más allá de cierto límite, ya que la lógica conserva el estado y no es extraíble en una función o algún otro componente . A veces, quienes dicen que React no logra una "separación de funciones" se quejan de esto.
Tales casos son muy comunes y están asociados, por ejemplo, con animación, procesamiento de formularios, conexión a fuentes de datos externas y muchas otras operaciones que podemos necesitar realizar con nuestros componentes. Tratando de resolver tales problemas solo con componentes, generalmente obtenemos:
- Componentes gigantes que son difíciles de refactorizar y probar.
- Duplicación de lógica entre varios componentes y métodos de ciclo de vida.
- Patrones complejos , en particular, accesorios de renderizado y componentes de orden superior.
Creemos que los interceptores son los más prometedores para resolver todos estos problemas.
Los interceptores ayudan a organizar la lógica dentro del componente en forma de unidades aisladas reutilizables :

Los interceptores están en línea con la filosofía React (flujo y composición de datos explícitos) y dentro de un componente, no solo entre componentes . Es por eso que me parece que los interceptores encajan naturalmente en el modelo de componente React.
A diferencia de los patrones como las propiedades de representación o los componentes de orden superior, los rastreadores no cargan su árbol de componentes con archivos adjuntos innecesariamente profundos. Además, no tienen las
desventajas inherentes a las impurezas.
Incluso si a primera vista los interceptores te deforman (¡como yo al principio!) Recomiendo darle una oportunidad a esta opción y experimentar con ella. Creo que te gustará.
¿Reacciona la hinchazón debido a los interceptores?Hasta que hayamos cubierto los interceptores en detalle, es posible que le preocupe que agregar interceptores en React sea solo una multiplicación de entidades. Esta es una crítica justa. Creo que esto: aunque, a corto plazo, realmente sientes una carga cognitiva adicional (para estudiarlos), al final solo te sentirás mejor.
Si los interceptores se arraigan en la comunidad React, entonces, de hecho , se reducirá el número de entidades que deben gestionarse al escribir aplicaciones React . Usando interceptores, puede usar constantemente funciones, en lugar de cambiar entre funciones, clases, componentes de orden superior y representación de componentes.
En cuanto al aumento en el tamaño de la implementación, la aplicación React con el soporte de interceptores aumenta solo en aproximadamente ~ 1.5kB (min + gzip). Si bien esto en sí mismo no es demasiado, es muy probable que
al usar interceptores, el tamaño de su ensamblaje incluso disminuya , ya que el código del interceptor generalmente se minimiza mejor que el código equivalente que usa clases. El siguiente ejemplo es un poco extremo, pero demuestra claramente por qué todo es así (
haga clic para expandir todo el hilo):
No hay cambios revolucionarios en la propuesta de interceptor . Su código funcionará bien incluso si comienza a usar interceptores en componentes nuevos. De hecho, esto es exactamente lo que recomendamos: ¡no reescriba nada a nivel mundial! Sería aconsejable esperar hasta que se establezca el uso de interceptores en todos los códigos críticos. Sin embargo, le agradeceremos que pueda experimentar con la versión alfa 16.7 y dejarnos comentarios sobre la
propuesta de interceptores , así como
informar cualquier error .
¿Qué es - interceptores?Para comprender qué son los interceptores, debe retroceder un paso y pensar qué es la reutilización de código.
Hoy en día, hay muchas formas de reutilizar la lógica en las aplicaciones React. Entonces, para calcular algo, puede escribir funciones simples y luego llamarlas. También puede escribir componentes (que pueden ser funciones o clases). Los componentes son más potentes, pero al trabajar con ellos, debe mostrar alguna interfaz de usuario. Por lo tanto, el uso de componentes es inconveniente para transmitir lógica no visual. Entonces llegamos a patrones complejos como propiedades de representación y componentes de orden superior.
¿React no lo haría más fácil si solo hubiera una forma general de reutilizar el código, y no tanto?Las funciones parecen ser perfectas para el código reutilizable. Pasar la lógica entre funciones es la menos costosa. Sin embargo, el estado local de React no se puede almacenar dentro de las funciones. No puede extraer comportamientos como "rastrear el tamaño de la ventana y actualizar el estado" o "animar un valor durante algún tiempo" desde un componente de clase sin reestructurar el código o sin introducir abstracciones como Observables. Ambos enfoques solo complican el código, y React es amable con nosotros por su simplicidad.
Los interceptores resuelven este mismo problema. Gracias a los interceptores, puede usar las funciones React (por ejemplo, estado) desde una función, llamándola solo una vez. React proporciona varios interceptores integrados que corresponden a los ladrillos React: estado, ciclo de vida y contexto.
Debido a que los interceptores son funciones regulares de JavaScript, puede combinar los interceptores integrados provistos en React para crear "interceptores nativos" . Por lo tanto, los problemas complejos se pueden resolver con una sola línea de código y luego multiplicarlo en su aplicación o
compartirlo en la comunidad ReactPrecaución: estrictamente hablando, sus propios interceptores no están entre las características de React. La capacidad de escribir sus propios interceptores proviene naturalmente de su organización muy interna.
¡Muéstrame el código!Supongamos que queremos suscribir un componente al ancho actual de la ventana (por ejemplo, para mostrar otro contenido o un área de visualización más estrecha).
Hoy se puede escribir un código similar de varias maneras. Por ejemplo, para crear una clase, crear varios métodos de ciclo de vida, o tal vez incluso recurrir a propiedades de representación o aplicar un componente de orden superior si está buscando reutilizarlo. Sin embargo, creo que nada se compara con esto:
gist.github.com/gaearon/cb5add26336003ed8c0004c4ba820eaeSi lee este código, significa que hace exactamente lo que dice . Usamos el ancho de la ventana dentro de nuestro componente, y React redibuja su componente si cambia. Esto es precisamente para lo que se necesitan los interceptores: para que los componentes sean verdaderamente declarativos, incluso si contienen efectos estatales y secundarios.
Considere cómo se podría implementar este propio interceptor. Podríamos usar el estado Reaccionar local para mantener el ancho actual de la ventana, y establecer el estado cuando la ventana cambia de tamaño usando un efecto secundario:
gist.github.com/gaearon/cb5add26336003ed8c0004c4ba820eaeComo se muestra arriba, los interceptores integrados de React como
useState
y
useEffect
sirven como ladrillos. Podemos usarlos directamente desde nuestros componentes, o ensamblar nuestros propios interceptores a partir de ellos, por ejemplo,
useWindowWidth
. Usar sus propios interceptores no parece menos idiomático que trabajar con la API React incorporada.
Lea más sobre los interceptores integrados en
esta revisión .
Los interceptores están encapsulados: cada vez que se llama al interceptor, recibe un estado local aislado dentro del componente que se está ejecutando actualmente . En este ejemplo en particular, esto no es importante (¡el ancho de la ventana es el mismo para todos los componentes!), ¡Pero esto es precisamente en lo que radica el poder de los interceptores! Están destinados a separar no el estado, sino la lógica de preservación del estado.
¡No queremos romper el flujo de datos aguas abajo!Cada interceptor puede contener algunos efectos locales y estatales. Puede transferir datos entre múltiples interceptores, tal como se hace generalmente entre funciones. Pueden tomar argumentos y devolver valores porque son funciones de JavaScript.
Aquí hay un ejemplo de una biblioteca de animación React donde experimentamos con interceptores:
Observe cómo se implementa la impresionante animación en el código fuente que se muestra: pasamos valores entre múltiples interceptores nativos dentro de la misma función de representación.
codesandbox.io/s/ppxnl191zx(Este ejemplo se trata con más detalle en
esta guía ).
Debido a la capacidad de transferir datos entre interceptores, son muy convenientes para implementar animaciones, suscribirse a datos, administrar formularios y trabajar con otras abstracciones con estado.
A diferencia de las propiedades de representación o los componentes de orden superior, los interceptores no crean una "jerarquía falsa" en su árbol de representación . Se parecen más a una lista bidimensional de "celdas de memoria" adjuntas a un componente. No hay niveles extra.
¿Qué hay de las clases?En nuestra opinión, nuestros propios interceptores son el detalle más interesante de toda la oferta. Pero para que sus propios interceptores sean funcionales, React debe proporcionar a nivel de funciones la capacidad de declarar el estado y los efectos secundarios. Esto es exactamente lo que nos permite hacer interceptores
useState
como
useState
y
useEffect
. Lea más sobre esto en la
documentación .
Resulta que tales interceptores integrados son convenientes no solo al crear sus propios interceptores. También son suficientes para determinar los componentes en su conjunto, ya que nos proporcionan las capacidades necesarias, por ejemplo, el estado. Es por eso que nos gustaría que los interceptores se conviertan en el medio principal para definir los componentes React en el futuro.
No, no planeamos abolir gradualmente las clases. Usamos decenas de miles de componentes de clase en Facebook y nosotros (al igual que usted) absolutamente no queremos reescribirlos. Pero, si la comunidad React comienza a usar interceptores, será inapropiado preservar las dos formas recomendadas de escribir componentes. Los interceptores cubren todos los casos prácticos en los que se usan las clases, pero brindan una mayor flexibilidad al extraer, probar y reutilizar el código. Es por eso que conectamos los interceptores con nuestras ideas sobre el futuro de React.
¿Qué pasa si los interceptores son mágicos?Quizás
las reglas del interceptor lo desconcertarán.
Aunque no es habitual llamar a un interceptor en el nivel superior, probablemente no desee determinar la condición en la condición usted mismo, incluso si pudiera . Por ejemplo, no se puede determinar un estado sujeto a una condición en el aula, y durante cuatro años de comunicación con los usuarios de React, no he escuchado ninguna queja al respecto.
Tal diseño es crítico para introducir sus propios interceptores sin introducir ruido de sintaxis excesivo o crear trampas. Entendemos que por costumbre es difícil, pero creemos que este compromiso es aceptable, dadas las oportunidades que ofrece. Si no está de acuerdo, le sugiero que experimente y pruebe cómo le gusta este enfoque.
Hemos estado usando ganchos de producción durante un mes para ver si las nuevas reglas confundirán a los programadores. La práctica muestra que una persona domina con interceptores en cuestión de horas. Confieso que a primera vista estas reglas me parecían herejías, pero este sentimiento pasó rápidamente. Esa fue la impresión que tuve cuando conocí a React. (¿No te gustó React? Y solo me gustó por segunda vez).
Tenga en cuenta: a nivel de implementación de interceptores tampoco hay magia. Según
Jamie , ella obtiene algo como esto:
gist.github.com/gaearon/62866046e396f4de9b4827eae861ff19Mantenemos una lista ampliada de interceptores y pasamos al siguiente componente de la lista cada vez que utiliza un interceptor. Gracias a las reglas de los interceptores, su orden es el mismo en cualquier motor de renderizado, por lo que con cada llamada podemos proporcionar el componente con el estado correcto.
(
En este artículo de Rudy Yardley, ¡ todo está bellamente explicado en las fotos!)
Quizás te preguntaste dónde React almacena el estado de los interceptores. Donde está el estado de las clases. React tiene una cola de actualización interna que contiene la verdad definitiva para cada estado, independientemente de cómo defina sus componentes.
Los interceptores son independientes de los proxies y captadores, que son tan comunes en las bibliotecas JavaScript modernas. Por lo tanto, se puede argumentar que hay menos magia en los interceptores que en otros enfoques populares para resolver tales problemas. No más que en
array.push
y
array.pop
(¡en el caso de que el orden de las llamadas también sea importante!)
El diseño del interceptor no está vinculado a React. De hecho, unos días después de la publicación de la propuesta, una variedad de personas nos mostró implementaciones experimentales de la misma API de interceptor para Vue, componentes web e incluso funciones comunes de JavaScript.
Finalmente, si está fanáticamente dedicado a la programación funcional y se siente incómodo cuando React comienza a confiar en un estado mutable como parte de una implementación. Pero puede tranquilizarle que el procesamiento del interceptor se pueda implementar en su forma pura, limitándose a los efectos algebraicos (si fueran compatibles con JavaScript). Naturalmente, a nivel intra-sistema, React siempre se basó en un estado mutable, y eso es exactamente lo que le gustaría evitar.
Independientemente del punto de vista que esté más cerca de usted, pragmático o dogmático, espero que al menos una de estas opciones le parezca lógica. Lo más importante, en mi opinión, los interceptores simplifican nuestro trabajo, y se vuelve más conveniente para los usuarios trabajar. Eso es lo que los interceptores me sobornan así.