Video Summary and Transcription
Esta charla analiza el uso de Relay y fragmentos en la obtención de datos para aplicaciones React. Explora los desafíos de obtener datos de los componentes y la optimización de la obtención de datos. La charla también aborda el uso de fragmentos para la obtención de datos de componentes y los desafíos y errores potenciales que pueden surgir. Destaca las decisiones de diseño de Relay que facilitan el acceso a los datos y el uso de variables en los fragmentos. La charla concluye con una discusión sobre la obtención de datos de fragmentos y la combinación de fragmentos y consultas en Relay.
1. Introducción a Relay y Hasura
Voy a hablar de enamorarse de Relay, mi viaje desde el escepticismo hasta la admiración. Me presentaré como el fundador y CEO de Hasura, una startup de tecnología de código abierto. Hasura proporciona un motor GraphQL que se conecta a tu base de datos y servicios, ofreciendo una API GraphQL unificada. Relay se encarga de lograr la mejor obtención de datos mientras se utiliza GraphQL. Utilizaré un ejemplo de un panel de datos para demostrar el poder de Relay en una aplicación de React.
Hola a todos. Estoy muy feliz de estar aquí. Espero que todos estén bien. Voy a hablar un poco sobre enamorarse de Relay. Así que este es mi viaje de ser escéptico sobre Relay como un cliente GraphQL y luego gradualmente enamorarme de él.
Mi nombre es Tanna Gopal, y antes de comenzar, les daré una breve introducción. Soy el fundador y CEO de Hasura. Hasura es una startup de tecnología de código abierto. Construimos un motor GraphQL que puede conectarse principalmente a tu base de datos y otros servicios para que puedas unificar tu API GraphQL. Se ejecuta como un contenedor Docker en tu propia infraestructura. Es de código abierto bajo la licencia Apache, y puedes consultarlo en GitHub. Gran parte de este trabajo y esta charla han sido motivados por el hecho de que hemos estado agregando soporte para Relay en Hasura, y al final te mostraré cómo puedes comenzar a jugar con Relay y Hasura.
Bien, ahora vamos a sumergirnos en Relay. Cuando piensas en GraphQL e integras GraphQL en tus aplicaciones, el mayor punto de venta de GraphQL es que, al menos para mí, fue la primera vez que encontré una API fácil de explorar e integrar, ¿verdad? Es como si pudiera ver cómo es la API, puedo autocompletarla, puedo entender cómo debo integrarla porque puedo ver los tipos, ¿verdad? Puedo integrarlo con mi sistema de tipos, lo que sea que esté usando en el lado del cliente. Y cosas así. Donde encaja Relay es que Relay se encarga de la responsabilidad o Relay facilita lograr la mejor obtención teórica de datos, ¿verdad? Que podemos lograr mientras usamos GraphQL y nos mantenemos cuerdos. Voy a hablar sobre el aspecto de mantenernos cuerdos, ¿verdad? Entonces, ¿cómo podemos, al usar GraphQL en nuestra aplicación, cuál es la mejor obtención de datos que podemos hacer al introducir la menor cantidad de carga en nosotros mismos como desarrolladores, ¿verdad? Ahí es donde encaja Relay. Y esto se volverá más claro a medida que avancemos y es por eso que Relay es increíble y las ideas detrás de Relay son realmente increíbles.
Para motivar o ver un ejemplo a lo largo de esta charla, lo que voy a hacer es tomar el ejemplo de un panel de datos, ¿verdad? Esta es una aplicación de front-end. Es una aplicación de React. Es un panel de administración de datos, ¿verdad? Puedes ver que hay tablas a la izquierda. Estoy mirando una tabla en particular. Esa tabla tiene columnas, esa tabla tiene una opción de filtrado, ¿verdad? Hay cosas así. Esta aplicación es en realidad la consola de Hasura, que es una aplicación de React. Pero en cualquier caso, imagina que esta es una aplicación de React de complejidad media, solo porque hay mucha obtención de datos sucediendo. Si miras los recuadros rojos que he resaltado aquí, déjame mostrarlo con mi puntero. Si miras estos recuadros rojos que he resaltado aquí, esos son los diferentes fragmentos de datos que estamos obteniendo de nuestra API. Aquí estamos obteniendo una lista de todas las tablas. Aquí estamos obteniendo información sobre esa tabla en particular. Aquí solo estamos mostrando el nombre de la tabla. Aquí estamos obteniendo una lista de todas las columnas para que podamos renderizar este menú desplegable y el tipo de columna para que podamos renderizar las operaciones en las que puedes filtrar esa columna.
2. Obtención de datos para la vista de tabla
Aquí estoy obteniendo una lista de columnas para renderizar una vista de tabla. En la vista de tabla modificada, estoy viendo diferentes columnas y sus tipos en la base de datos. También estoy obteniendo una lista de atributos y, para un atributo, una cantidad mayor de datos. Para obtener estos datos y construir la aplicación, adjuntaría una consulta a cada componente de la interfaz de usuario.
Correcto. Y aquí estoy obteniendo una lista de todas las columnas para poder renderizar una vista de tabla. Correcto. Estas son las diferentes piezas de datos que necesito obtener para esta aplicación.
Veamos otra vista en la misma aplicación. Ahora estoy en una vista diferente. Estoy en la vista de tabla modificada. Correcto. Aquí estoy viendo las diferentes columnas y los tipos de esas columnas en mi base de datos. Correcto. Estoy enumerando todas las columnas diferentes. Estoy enumerando cuáles son los tipos de esas columnas. Correcto. Ya sea una columna de texto, una columna de caracteres o si es nula. Y luego, en un caso, una de las columnas en las que he hecho clic como usuario, tengo una vista expandida. En esta vista expandida, no estoy viendo solo dos propiedades de esta columna. Estoy viendo como cinco propiedades diferentes de esta columna. Correcto.
Así que estoy obteniendo una lista de atributos. Pero para uno de esos atributos, estoy obteniendo una cantidad mayor de datos. Correcto. Nuevamente, un escenario bastante típico que puedes imaginar en una aplicación. Correcto. Muy bien. Veamos cómo habríamos utilizado una API de GraphQL para obtener estos datos y construir esta aplicación. Correcto. La primera opción, lo más sencillo que podría haber hecho, es simplemente tomar cada componente de la interfaz de usuario y adjuntarle una consulta. Hago una consulta aquí para obtener las tablas. Hago una consulta aquí para obtener el nombre de la tabla para un ID en particular. Correcto.
3. Obtención de datos desde componentes
Estoy obteniendo datos de diferentes componentes, lo que resulta en múltiples consultas. Este enfoque es fácil pero terrible porque genera un gran número de llamadas de red y consultas redundantes.
Tal vez estoy obteniendo este ID desde la URL o desde un componente en particular en el que se hizo clic. Correcto. Desde este componente en el que se hizo clic. Luego, creo otro componente para esta columna de filtro, una especie de interfaz de usuario en la que obtengo el nombre y el tipo de las columnas. Y aquí, para este tipo de explorador de tablas, hago una consulta para obtener los nombres de las columnas. Correcto. Así que estoy haciendo diferentes consultas. Es realmente fácil porque todos los data que necesito obtener están a nivel de componente. Correcto. Por lo tanto, la ventaja de tener una consulta por componente es que, ya sabes, es súper fácil. Correcto. Es modular, por así decirlo. En realidad, esto, por supuesto, de hecho, es una idea terrible porque estás haciendo un número tremendo de llamadas de red y va en contra de todo el propósito. Y, ya sabes, las cosas que la gente dice sobre GraphQL. De hecho, no solo hace muchas llamadas de red. Si observas esta interfaz de usuario detenidamente, verás que estoy haciendo muchas llamadas redundantes. Correcto. Estoy obteniendo el nombre aquí, que ya he obtenido aquí y obteniendo los nombres y tipos de las columnas aquí, que ya he obtenido aquí. Así que también estoy haciendo consultas redundantes. Correcto. Aparte de que estoy haciendo múltiples consultas, lo cual es terrible. Correcto.
4. Optimización de la obtención de datos
La primera optimización consiste en realizar una consulta gigantesca que obtenga todos los datos necesarios para la página. Si bien teóricamente esta es la mejor consulta, resulta inconveniente para los desarrolladores que crean diferentes componentes, ya que los requisitos de datos para los componentes secundarios están dispersos en los componentes ancestros. Pasar props a los componentes secundarios se vuelve engorroso.
Entonces. Entonces, la primera optimización que puedo hacer. Correcto. La optimización más sencilla que puedo hacer es realizar una consulta gigantesca. Así que hago una consulta gigantesca para esta página. Correcto. Y teóricamente, esta es la mejor consulta que puedo hacer. Correcto. Así que hago una consulta. Obtengo todas las tablas y sus nombres para una tabla en particular que quiero mostrar. Obtengo el nombre de la tabla y obtengo el tipo de columnas, ya sabes, los nombres de las columnas que voy a mostrar aquí en el explorador de tablas y los tipos que voy a usar para mostrar los diferentes operadores. Correcto. Entonces, si es un entero, puedo hacer igual. Puedo hacer igual si es un booleano, mostraré verdadero, falso, lo que sea. Entonces. Esta es una consulta óptima que puedo hacer. Esto es bueno. Correcto. Esto es bueno, pero no me gusta esta idea porque aunque es óptima, como desarrollador que está construyendo así, como desarrollador que está construyendo estos diferentes componentes, es inconveniente porque los requisitos de data para un componente secundario, ¿verdad? Por ejemplo, una interfaz de usuario de columna de filtro o una interfaz de usuario de explorador de data, los requisitos de ese data están en algún lugar del ancestro. Correcto. Está en algún componente ancestro que tiene, que tiene esto, que está haciendo esta consulta. Correcto. Y luego voy a pasar todas las props a todos los componentes secundarios gradualmente. No es una experiencia muy divertida. Correcto.
5. Introducción de fragmentos para la obtención de datos del componente
Puedo introducir fragmentos en lugar de tener una consulta gigantesca en el nivel superior. Cada componente hijo puede tener su propio fragmento que especifique los datos que necesita. Por ejemplo, el componente de encabezado de la tabla puede declarar un fragmento para el nombre, mientras que la interfaz de usuario del filtro puede tener un fragmento para las columnas y los tipos. Esto permite un control más granular sobre los datos obtenidos para cada componente.
Entonces, lo siguiente que puedo hacer es introducir fragmentos. Correcto. Así que lo que puedo hacer es, en lugar de tener una consulta gigantesca en el nivel superior, tendré una consulta en el nivel superior que haga referencia a diferentes fragmentos que tengo. Correcto. Entonces, cada vez que construyo un componente hijo, digamos que estoy construyendo el componente de encabezado de la tabla. Declaro un fragmento que dice, aquí hay un fragmento, quiero el nombre. Correcto. Si estoy construyendo la interfaz de usuario del filtro, diré, bueno, aquí hay un fragmento. Quiero todas las columnas, los nombres de las columnas y los tipos. Correcto. Así puedo renderizar esta interfaz de usuario. Si estoy construyendo este componente de explorador de tablas, básicamente solo obtengo los nombres de las columnas. Eso es todo lo que necesito para crear este encabezado de tabla. Correcto.
6. Uso de fragmentos en la construcción de componentes
Declaro fragmentos junto con los componentes que construyo. Cada componente puede declarar los datos exactos que necesita. Sin embargo, el uso de fragmentos puede propiciar errores. Dos puntos problemáticos son la importación de fragmentos y el acceso de los componentes hijos a todos los datos. Por ejemplo, un componente hijo para votar en comentarios debe incluirse en el fragmento principal.
Entonces, declaro estos fragmentos junto con los componentes que construyo. Correcto. Digamos que tengo tres archivos aquí, en cada uno de estos archivos incluyo un fragmento. Correcto. Además, si estoy construyendo el componente ancestral, iré al componente de consulta ancestral en algún lugar. Correcto. Aunque estos componentes tienen una jerarquía. Tengo que incluir, ya sabes, este fragmento en el componente ancestral aquí o algo así. Correcto. Eso es más o menos cómo sería la experiencia con los fragmentos.
Los beneficios aquí son que es agradable porque, ya sabes, cada componente que construyo, puedo declarar los data. Puedo declarar exactamente los data que quiero. Sin embargo, los inconvenientes son que la experiencia de usar fragmentos no es particularmente buena. Y a menudo propicia errores. Voy a hablar de dos puntos problemáticos particulares en la integración básica de fragmentos. Correcto. El primero es la importación de fragmentos. Y el segundo es que todos los componentes hijos tendrán acceso a todos los data. Así que veamos qué significa esto.
Correcto. Entonces, el primero, y este es un ejemplo que he tomado de la página de documentación de fragmentos del cliente Apollo. Si miras a la izquierda aquí, estoy haciendo, tengo un componente hijo. Así que imagina que estos son comentarios y los comentarios tienen votos. Correcto. Y lo que estoy viendo es, estoy viendo el componente hijo donde estoy votando en un comentario en particular. Así que esto tiene un fragmento. Correcto. Y luego el padre tiene la consulta completa, o tal vez un fragmento padre, ¿verdad? Tiene un fragmento más grande que incluye este fragmento. Correcto. Entonces, esta parte es necesaria, ¿verdad? Necesito incluir mi fragmento hijo en el fragmento padre.
7. Importación de fragmentos y posibles errores
Esta es la parte que resulta un poco irritante. Necesito incluir una importación de este fragmento como una variable dentro de este fragmento. Esto no es divertido porque tengo que asegurarme de incluir el fragmento dentro de mis componentes y también importarlo en el componente padre, lo que puede generar errores.
Correcto. Pero esta es la parte que resulta un poco irritante. ¿Verdad? Necesito declarar, necesito incluir una importación de este fragmento como una variable dentro de este fragmento. Esto es irritante, ¿verdad? Esto no es divertido. Porque ahora tengo que asegurarme de que no solo incluyo el fragmento dentro de mis componentes, sino que también, cada vez que escribo el componente padre, estoy importando el, también estoy importando ese fragmento e incluyendo ese fragmento aquí también, ¿verdad? Lo cual puedo olvidar hacer, o, ya sabes, pueden ocurrir todo tipo de cosas. Pueden haber errores.
8. Desafíos con el uso de fragmentos y paso de datos
Si tengo fragmentos que incluyo en la consulta de nivel superior, todos los componentes tendrán acceso a más atributos de los necesarios. Pasar props a los componentes secundarios requiere cuidado para evitar errores. Para facilitar el uso de fragmentos, necesitamos importación y validación automática, así como optimizaciones para el paso de datos.
Correcto. Estos son un poco los, no es una experiencia ideal. Correcto.
Hay otro punto doloroso. El otro punto doloroso es que si tengo fragmentos que incluyo en la consulta de nivel superior, lo que sucederá es que todos estos componentes, los componentes de React donde, digamos, obtengo estos componentes y les asigno una variable para obtener los datos de la tabla, ¿verdad? Cuando obtengo estos datos y les asigno una variable, este componente terminará teniendo acceso, no solo al atributo de nombre que necesita, sino también al atributo de columnas y también a columnas.nombre y columnas.tipo. Correcto.
Y si observo el componente de filtro de columnas que solo necesita nombre y tipo, está bien. Tendré acceso a columnas.nombre y columnas.tipo, pero el componente de explorador de columnas que solo necesita acceso al nombre de la columna, también terminará teniendo acceso al tipo de columna. Correcto. Y debo tener cuidado con la forma en que paso estas props a mis componentes secundarios, desde la consulta principal que tengo. Correcto. Y requiere un poco de cuidado. Y si no tengo cuidado, lo que a menudo puede suceder, especialmente cuando estamos construyendo aplicaciones rápidamente, ya sabes, digamos que estoy construyendo este componente de explorador y me doy cuenta de que necesito acceso al tipo de columna. Tal vez olvidé actualizar el fragmento. Correcto. Tal vez necesito ir a tablas.tabla.columnas.tipo de todos modos, porque tengo acceso a eso. Correcto. Debido a algún otro fragmento que estaba usando. Sería negligente. Correcto. O podría ser negligente y olvidar incluir ese atributo en este fragmento aquí. Correcto. Solo porque alguien más lo estaba solicitando. Y este será el punto de partida de los errores tan pronto como tengamos un gran número de fragmentos. Entonces, si queremos facilitar el uso de fragmentos, lo que realmente queremos hacer es asegurarnos de que los fragmentos se importen y validen automáticamente a medida que los usamos en nuestros componentes. Y también queremos que ocurran optimizaciones cuando estamos pasando data. Correcto. Para que obtengamos algún tipo de aislamiento y modularidad en los componentes que estamos escribiendo. Correcto. Para que cuando sienta que estoy escribiendo un componente para un fragmento, sienta que solo tengo acceso a los datos que declaré en mi fragmento.
9. Decisiones de diseño de Relay para el acceso a datos
Relay tiene dos decisiones de diseño que facilitan el acceso a los datos. Primero, tiene un compilador que se ejecuta como parte del proceso de compilación, asegurando que todos los fragmentos se importen y validen en tiempo de compilación. Segundo, al usar un fragmento en un componente, solo se puede acceder a los datos que están disponibles. Esto proporciona una forma conveniente de trabajar con fragmentos en Relay.
Correcto. Y aparte de eso, no tengo acceso a ningún otro data. A menos que se me pase explícitamente desde el componente padre. Correcto. Y si observas Relay, tiene dos decisiones de design que hacen que esto sea bastante fácil. La primera es que Relay no es solo un cliente de GraphQL que es una biblioteca que puedes incluir, crear consultas de GraphQL y hacer consultas de GraphQL, sino que Relay también tiene un compilador que hace posible que el compilador se ejecute como parte del proceso de compilación. Correcto. Se ejecuta en paralelo mientras realizas un flujo de trabajo de desarrollo. Y a medida que construyes fragmentos e importas consultas, todas las consultas, todos los fragmentos que se importan, todos los fragmentos que se validan, todo ocurre en tiempo de compilación para que no obtenga estos errores de tiempo de ejecución con la importación de fragmentos. Correcto. O por ejemplo, cuando pienso en el enmascaramiento de data, cuando uso el fragmento en mi componente, solo puedo usar los data a los que tengo acceso, que es la forma en que uso el contenedor de fragmentos dentro de Relay. Correcto.
10. Usando Fragmentos e Introduciendo Variables
Cuando uso fragmentos con Relay, no necesito importarlos explícitamente en mi fragmento o consulta principal. Solo tengo acceso a los atributos declarados en mi fragmento. Esto asegura que obtenga los datos que necesito y evita errores. Sin embargo, a medida que comienzo a usar fragmentos, pronto necesitaré variables en mis fragmentos. Por ejemplo, en un panel de control, es posible que desee que los usuarios elijan ciertas opciones.
Para ver de manera tangible el trabajo que se me quita. No tengo que importar explícitamente fragmentos en mi fragmento principal o en mi consulta principal. Y cuando intento acceder a los data que mi componente en particular obtendrá. Correcto. Solo tendré acceso a aquellos atributos que he declarado en mi fragmento. Correcto. Aquí solo tengo acceso a table.name. Aquí solo tengo acceso a columns.name y columns.type. Y aquí solo tengo acceso a columns.name nuevamente. Correcto. Lo cual es bueno porque si intento acceder a alguna otra propiedad, obtendré un error en tiempo de compilación. Correcto. Lo cual es genial. Correcto. Entonces, cuando usas fragmentos con Relay, esto comenzará a funcionar automáticamente. Correcto. No quería entrar en más sintaxis para mostrarte cómo es la configuración, pero solo quería enfatizar en los conceptos que podrás usar. Muy bien.
El siguiente problema con el que te encontrarás al comenzar a usar fragmentos. Correcto. Los fragmentos son buenos. Estoy usando fragmentos en todas partes. Es genial. Mis componentes son modulares. Correcto. Pero el siguiente problema con el que me encontraré es que pronto necesitaré variables en mis fragmentos. Correcto. Entonces, lo que necesitaré es hablar sobre un ejemplo simple. Digamos que en este tipo de panel de control que tengo. Quiero que los usuarios elijan.
11. Usando Variables en Fragmentos
Quieres que los usuarios puedan ver solo las primeras tres columnas. Si deseas usar una variable en un fragmento, debes declararla en el nivel superior, lo cual puede ser incómodo porque estas variables pertenecen a lo más profundo de la jerarquía de componentes y provienen de la entrada del usuario.
Sabes que quiero que los usuarios puedan ver solo las primeras tres columnas. Tal vez tenga una tabla de 1,000 columnas. Correcto. Y solo quiero ver las primeras tres columnas. Correcto. Entonces tal vez quiero hacer un límite, es decir, quiero obtener solo las primeras tres. Correcto. Solo quieres las primeras tres columnas. Correcto. Y si eso es lo que quieres, te darás cuenta de que necesitas tener una variable que este fragmento en particular tenga. Correcto. Tal vez este componente en particular tenga algo como una interfaz de usuario que diga, ¿cuántas columnas quieres ver? Quiero ver tres. Quiero ver diez. Entonces elijo la cantidad de columnas que quiero ver y eso se convierte en una variable proporcionada por el usuario. Correcto. Para hacer el fragmento.
La cosa es que esto obviamente es posible de hacer. Esta es una sintaxis válida. Pero si quiero usar una variable en un fragmento, debo declararla en el nivel superior. Correcto. Eso significa que si necesito usar una variable en un fragmento en algún lugar profundo de mi jerarquía de componentes, debo declarar esa variable en la consulta de nivel superior. Correcto. Porque necesito declarar variables de consulta, lo cual, como puedes imaginar, es un poco irritante. Correcto. Porque estas variables pertenecen a algún lugar profundo en la jerarquía de componentes. Estas variables provienen de la entrada del usuario. Estas variables no pertenecen a un componente ancestro. Correcto. Imagina si volviéramos a nuestro diseño de consulta original. Correcto.
12. Declarando Variables para Fragmentos Localmente
Si tuviera una consulta por componente, estaría realizando una cantidad absurdamente grande de solicitudes, pero podría declarar exactamente las variables que necesito. Me gustaría declarar variables para mis fragmentos localmente, a nivel de fragmento, y usarlas allí. Relay proporciona un concepto de diseño de tener argumentos y definición de argumentos, lo que te permite definir y declarar localmente una variable en un fragmento sin modificar la consulta de nivel superior. Esto es realmente genial.
Si tuviera una consulta por componente, estaría realizando una cantidad absurdamente grande de solicitudes, pero podría declarar exactamente las variables que necesito, ya sabes, cuando las necesito. Correcto. Lo cual es, lo cual es genial. Entonces, si, si la forma en que esto funciona en Relay es, la forma en que esto funciona en Relay es lo que me gustaría hacer es poder declarar variables para mis fragmentos localmente. Correcto. Me gustaría declararlas a nivel de fragmento y me gustaría poder usarlas también a nivel de fragmento. Y aquí es donde un concepto de diseño de Relay de tener argumentos y definición de argumentos. Estos son directivas del lado del cliente y dos directivas del lado del cliente que puedes agregar a cualquier fragmento para definir y declarar localmente una variable, en ese mismo lugar, correcto. A nivel de fragmento sin tener que ir a la consulta de nivel superior y modificar eso. Y eso es realmente genial. Y veremos un ejemplo pronto. Correcto. Y veremos un ejemplo de cómo se ve la sintaxis pronto. Pero por ahora, en el próximo, en el siguiente ejemplo de IU que veremos.
13. Manejo de Variables Localmente y Actualización de Fragmentos
Necesitamos una forma de manejar variables localmente y actualizar fragmentos cuando el usuario proporciona información. Por ejemplo, al paginar a través de una lista, las variables utilizadas para obtener la lista cambian y queremos volver a obtener la lista sin ejecutar toda la consulta nuevamente. Este deseo de consultas casi para cada componente conduce a la necesidad de modularidad de fragmentos y la capacidad de ciclo de vida independiente de las consultas.
Pero por ahora, ten en cuenta que necesitamos alguna forma de poder manejar variables localmente. Correcto. Entonces, tan pronto como te des cuenta de que quieres manejar variables localmente, te das cuenta de que en realidad lo que querías hacer era cada vez que el usuario proporciona información en algún lugar en lo profundo de la jerarquía de componentes, ¿verdad?, lo que quieres hacer es actualizar ese fragmento. Correcto. Entonces, si observas ese tipo de ejemplo en el que quería ver solo tres columnas, tal vez el usuario nos proporcione más información y quiero ver solo 10 columnas. Y a medida que el usuario nos proporciona información, queremos actualizar solo ese fragmento y no queremos volver a ejecutar, definitivamente no queremos ejecutar toda la consulta nuevamente. Correcto. Eso es lo que queremos hacer. Correcto. Entonces, queremos utilizar la modularidad de los fragmentos. Pero queremos tener casi la capacidad de ciclo de vida independiente de una consulta. Correcto. Y veamos un ejemplo un poco más tangible. Correcto. Entonces, en las aplicaciones que estás construyendo, frecuentemente verás esto cuando se trata de la paginación. Correcto. Cuando quieres paginar, tienes un componente hijo que está renderizando una lista. Correcto. Y a medida que paginas, las variables que estás utilizando para obtener esa lista, esas variables están cambiando. Correcto. Y esas variables están cambiando según la demanda. Ahora, cuando esas variables cambian, quieres volver a obtener la lista. No quieres ejecutar toda la consulta nuevamente. Correcto. Ese es el tipo de cosa del que estaba hablando, que es como un caso de uso de expansión. Correcto. O un caso de uso de `leer más`. Y como dije, esto es como querer tener consultas casi para cada componente. Correcto. Entonces, veamos un ejemplo tangible y cómo se ven las consultas.
14. Obtención de Atributos Expandidos
Tengo una lista donde quiero ver más información sobre un atributo cuando el usuario hace clic en expandir. Por defecto, solo quiero dos atributos para cada elemento de la lista, pero si se expande, quiero cinco atributos. Ejecuto una consulta para obtener todas las columnas, donde obtengo información mínima como el nombre y la descripción. Si un elemento se expande, quiero mostrar atributos adicionales. Esto se logra definiendo una variable a nivel de fragmento y utilizando definiciones de argumentos. En la carga inicial, el valor predeterminado es falso y cuando el usuario hace clic en expandir, se vuelve a ejecutar el fragmento.
Aquí estoy viendo un ejemplo similar, donde tengo una lista. Correcto. Y cuando el usuario hace clic en expandir, quiero ver más información sobre este atributo. Por defecto, solo quiero dos atributos para cada elemento de la lista. Y si lo expando, quiero cinco atributos para ese elemento de la lista. Correcto. Y esto es para el panel de control que estamos construyendo. Correcto.
Entonces, si pienso en esto, lo que quiero hacer es ejecutar una consulta para obtener todos los elementos. En este caso, los elementos son columnas. Quiero obtener todas las columnas. Correcto. Por ejemplo, quiero obtener información mínima por columna. Veamos cuál es la información mínima aquí. Solo quiero obtener el nombre y la descripción. Estos son los dos atributos que quiero mostrar en caso de que uno de los elementos esté expandido. Quiero poder mostrar información expandida para esa columna en particular. Y ahora veamos la forma expandida. Quiero ver el nombre y otros 10 atributos. Y esta es una variable que se define a nivel de fragmento. Estoy utilizando definiciones de argumentos para definir una variable en este fragmento y decir que, al renderizar esta lista, si esta variable es verdadera para esta columna en particular, también quiero incluir este fragmento en particular. En la carga inicial, el valor predeterminado será falso. Por lo tanto, en la carga inicial, obtendremos todos los elementos con solo el nombre y la descripción. Cuando el usuario hace clic en expandir, es cuando quiero volver a ejecutar este fragmento.
15. Volver a ejecutar y volver a obtener datos de fragmentos
Quiero volver a ejecutar y volver a obtener los datos de un fragmento solo si está expandido. Esto se puede lograr haciendo que el fragmento sea recuperable y utilizando la función de refetch con la variable establecida en true. El fragmento se ejecutará como una consulta con la variable establecida en true, lo que permitirá la recuperación de datos específicos. Este proceso ocurre cuando el fragmento está expandido.
Pero obtener más data para este fragmento. Correcto. Eso es, eso es más o menos lo que quiero hacer, ¿verdad? Quiero volver a ejecutar y volver a obtener los data para este fragmento en particular, solo si lo expandimos es verdadero para obtener más data, ¿verdad? Y, y realmente, eso es tan simple como ir a un fragmento en particular y decir, hagamos que este fragmento sea recuperable. Y cada vez que haya un clic o un evento de usuario, ejecutaremos una función llamada refetch, que es una función que tienes en un fragmento con la variable establecida en true. Entonces, este fragmento se ejecutará como una consulta, ¿verdad? Se ejecutará como una consulta con esta variable establecida en true para que puedas obtener exactamente estos data, ¿verdad? Y eso sucederá solo para esa columna, ¿verdad? Un caso en el que hago clic en expandir, ¿verdad?
16. Re-fetching de fragmentos y API de Relay
Los fragmentos en Relay no pueden ejecutarse de forma aislada y deben formar parte de una consulta más grande. Para habilitar el re-fetching de fragmentos, una API compatible con Relay implementa una interfaz de nodo con un ID globalmente único. Cuando se vuelve a obtener un fragmento, el cliente solo ejecuta la consulta para ese elemento específico, utilizando el ID único y los campos solicitados. Este enfoque combina la conveniencia de usar fragmentos con la capacidad de tratar cada fragmento como una consulta.
Y esto es realmente interesante, ¿verdad? Esto es, si piensas en cómo funciona en el fondo, si piensas en la ergonomía, es como, oh, este fragmento es como una consulta, así que puedo ejecutar esta consulta de nuevo. Ese es un proceso de pensamiento conveniente, ¿verdad? Pero en realidad, no es fácil hacer que funcione porque un fragmento no puede ejecutarse de forma aislada, ¿verdad? Un fragmento necesita ejecutarse como parte de una consulta más grande. Entonces, ¿cómo puede ejecutarse un fragmento por sí mismo? Y el secreto aquí es que una API de Relay normalmente implementa una interfaz de nodo y una resolución raíz de cualquier nodo. Entonces, una API compatible con Relay, bueno, ya no es obligatorio, es opcional, es que cada elemento que estás obteniendo tiene una interfaz o implementa una interfaz llamada nodo y tiene un ID globalmente único, ¿verdad? Y tu API de Relay GraphQL tiene un resolvedor raíz para obtener cualquier nodo solo por su tipo de GUID, su ID globalmente único. Entonces, lo que Relay puede hacer es que cuando marco o solicito al cliente que vuelva a obtener un fragmento en particular, el cliente no tiene que ejecutar toda la consulta de nuevo. El cliente solo vuelve a ejecutar la consulta para esa columna o ese elemento en particular que quiero volver a ejecutar. Y el cliente puede hacer eso porque cada elemento es una instancia de nodo y cada elemento tiene un ID único. Entonces, la consulta que se está ejecutando en segundo plano es en realidad consulta, elemento, ID único y los campos exactos que quiero. Entonces, en nuestro ejemplo particular, hay una consulta en segundo plano que se ejecuta cada vez que hacemos un re-fetch. La consulta que se está ejecutando, la consulta subyacente de GraphQL que se está ejecutando es una consulta en la columna donde el ID es igual al ID actual para esta columna que estoy renderizando, que es un ID geo que tengo. Y está obteniendo los campos nombre, descripción, tipo, anulable, único, predeterminado, lo que sea. Como está obteniendo los diferentes atributos que quiero. Y esto es realmente bueno. Obtengo la conveniencia de usar fragmentos, ¿verdad? Pero al mismo tiempo, obtengo la capacidad de tratar cada fragmento como una consulta. Y esto es realmente conveniente. Entonces, para resumir, si comienzas a mirar Relay, puede ser un poco abrumador. Si miras la documentación de Relay, si escuchas a las personas hablar de Relay, hay mucho sucediendo. No te preocupes demasiado por eso. Piensa en la modularidad que deseas en tu aplicación como la modularidad que puedes lograr con un buen enfoque basado en fragmentos. A medida que comienzas a pensar en esa modularidad y comienzas a pensar en usar fragmentos en la práctica, notarás que tendrás que reconstruir muchas de las mismas decisiones de diseño que Relay te impone desde el principio, ¿verdad? Por ejemplo, nombres únicos de fragmentos que nos permiten importar fragmentos automáticamente, ¿verdad? Tener directivas del lado del cliente que nos permitan declarar variables para fragmentos, tener la interfaz de nodo y un ID global único que nos permita volver a obtener y volver a ejecutar fragmentos según la demanda, eso facilita la paginación, facilita cosas como expandir un elemento, ¿verdad? Hace que todas esas interacciones sean más fáciles. Así que hoy estoy lanzando, hoy hemos estado trabajando muy duro en esto durante las últimas semanas, es que puedes probar Relay con Hasura. Así que ve a hasura.io/GraphQL/Relay. Todo lo que necesitas hacer es implementarlo en Heroku si solo quieres probar las cosas, configurar tu base de datos, hacer un poco de modelado de datos, establecer algunas relaciones entre estos modelos de datos que tienes, configurar las reglas de permisos, y obtendrás una API de Relay instantáneamente que puedes comenzar a usar e integrar. Y luego, por supuesto, más allá de las API de tipo CRUD que obtienes, una vez que quieras comenzar a agregar lógica de negocio personalizada, puedes agregarla en tu lenguaje favorito también, Node.js o Python o Ruby o lo que quieras, y desplegarlas en funciones sin servidor o donde sea, y eso se agregará a tu API de GraphQL con Hasura también. Y podrás probar Relay. Así que pruébalo. Danos tu opinión. Está en beta y deberíamos poder lanzarlo de manera estable muy, muy pronto. Para esta charla en particular, necesité mucha persuasión y comprensión, y obviamente la documentación de Relay ayuda, pero dos personas que me ayudaron especialmente fueron Gabriel y Sean, definitivamente deberías seguirlos en Twitter en ZTH y sgrove. Y también tienen dos publicaciones de blog muy buenas que han escrito juntos sobre Relay, que hablan sobre el cliente de GraphQL que hace el trabajo listo para ti y hablan sobre la paginación. Así que también deberías echarle un vistazo. Eso es todo de mi parte. Soy Tamay. Puedes encontrarme en Twitter. No dudes en contactarme si tienes alguna pregunta sobre GraphQL, Hasura, Relay y haré todo lo posible para responderlas o conectarte con personas que puedan responderlas. Gracias. Eso es todo de mi parte.
Comments