00:00:00Componentes de servidor de React. Ámalos u ódialos. Parece que últimamente hay más odio que
00:00:04amor, pero eso podría cambiar porque TanStack ha entrado en juego. Así es, ahora tenemos
00:00:08componentes de servidor de TanStack y han adoptado un enfoque bastante diferente al de Next.js. Vamos a verlo.
00:00:13Comenzaré con un párrafo de su publicación de anuncio que creo que tranquilizará a
00:00:21mucha gente. Dice: "La mayoría de la gente ahora piensa en los componentes de servidor de React de una manera
00:00:26donde el servidor es lo primero. El servidor es dueño del árbol, useClient marca las partes interactivas, y las convenciones
00:00:31del framework deciden cómo encaja todo. Esto convierte a los componentes de servidor de React de una
00:00:35primitiva útil en algo alrededor de lo cual tiene que orbitar toda tu aplicación. No creemos que debas tener que
00:00:40adoptar ese modelo completo por adelantado solo para obtener el valor de los componentes de servidor de React".
00:00:45Esencialmente, lo que dicen es que no quieren seguir la ruta de Next.js, donde es
00:00:48componentes de servidor por defecto y luego necesitas la directiva useClient donde quieres tener
00:00:52interactividad. En cambio, TanStack quiere pensar en ello como: ¿qué tal si pudieras usar componentes de servidor de React
00:00:57de forma tan granular como pudieras obtener JSON en el cliente? Con ese objetivo en mente, echemos un vistazo a
00:01:01cómo implementaron realmente los componentes de servidor porque, alerta de spoiler, realmente me gusta la forma en que
00:01:06lo hicieron. Lo que tengo aquí es una aplicación normal de TanStack Start, así que todo por el momento va a
00:01:10ser un componente de cliente y lo único que he hecho son algunos pequeños pasos de instalación necesarios para
00:01:15hacer funcionar los componentes de servidor, que es esencialmente solo instalar algunos paquetes y modificar tu
00:01:18vconfig. Así es como se ve la página actualmente, tenemos nuestro componente de saludo aquí, que
00:01:22debería ser actualmente un componente de cliente y en el código es literalmente solo un único componente de React
00:01:27luego aquí abajo tenemos una ruta normal de TanStack y estamos usando el componente de saludo aquí. Ahora, supongamos que
00:01:32en nuestro componente de saludo quisieras hacer algo de lógica en el servidor. En mi caso, quiero obtener el
00:01:36nombre de host del sistema operativo y también algunas variables de entorno que solo están disponibles en el servidor
00:01:40solo para mostrarles que esto realmente se está ejecutando allí. En este momento, si intento usar os.hostname
00:01:45no va a funcionar, ya que esta es una función de Node y no está disponible en el navegador.
00:01:49Lo que necesitamos hacer entonces es tomar nuestro componente de saludo y renderizarlo en el servidor y el
00:01:53primer paso para hacerlo en TanStack Start es con una simple función de servidor. Como pueden ver, tengo
00:01:58una aquí llamada get greeting y todo lo que estamos haciendo dentro es usar la nueva función render server
00:02:01component, poniendo nuestro componente dentro de ella y devolviendo el componente de servidor renderizable
00:02:06que obtenemos. Pueden pensar en esto como algo tan simple como crear una solicitud GET para nuestro componente.
00:02:10Luego, todo lo que necesitamos hacer es simplemente obtener el componente de la función de servidor que creamos
00:02:14y podemos hacerlo dentro de un cargador (loader) en una ruta aquí, así que simplemente esperamos (await) get greeting y luego
00:02:18devolvemos eso también y sigue siendo un componente de servidor renderizable. Luego podemos usar eso dentro de
00:02:23nuestra ruta aquí usando use loader data y simplemente usamos el componente aquí abajo como esto. Con eso,
00:02:27ahora tenemos nuestro primer componente de servidor de TanStack. Pueden ver que os.hostname ahora está funcionando y también está
00:02:32obteniendo variables de entorno que solo están disponibles en el servidor. Ahora, lo que
00:02:36absolutamente me encanta de esta implementación es que, si se fijan, lo único nuevo aquí es la función render
00:02:41server component. El resto de esto era solo TanStack Start normal. Podrías reemplazar esto
00:02:46con simplemente algunos datos JSON siendo devueltos y los obtendrías de la misma manera. También creo
00:02:51que esta implementación es muy explícita sobre dónde se está ejecutando realmente tu código. Lo haces todo
00:02:55dentro de una función de servidor, así que está bastante claro que se ejecutará en el servidor y también
00:02:59dentro del render server component. De hecho, creo que puedo mejorar mi código de demostración aquí simplemente
00:03:03tomando las funciones que estamos ejecutando aquí arriba y que quiero ejecutar en el servidor, poniéndolas
00:03:07dentro de la función de servidor y luego simplemente pasando esos valores a mi componente de saludo como props, así
00:03:12ahora mi componente de saludo es esencialmente solo un componente simple que puede ser utilizado en el
00:03:16cliente o en el servidor. Estoy ejecutando toda la lógica que quiero ejecutar en el servidor dentro de mi
00:03:21función de servidor aquí. Nuevamente, es bastante explícito que eso se ejecutará en el servidor. Esto
00:03:25literalmente se siente como el polo opuesto de la lógica utilizada en Next.js y me encanta.
00:03:30Significa que puedo pensar en React de una manera normal, todo es primero cliente y luego añadir componentes de servidor
00:03:34encima cuando los quiero. ¿Pero qué pasa si quisiera usar un componente de cliente dentro de mi componente de servidor,
00:03:38o sea, anidado dentro del árbol? Digamos que quisiera añadir un botón de contador aquí que cada vez que
00:03:43hacemos clic solo añade al contador. Bueno, si simplemente intento añadir esto en mi componente de servidor aquí con
00:03:47un onClick y luego también una llamada a useState, pueden ver que se rompe por completo. Dice que useState no es
00:03:52una función o que su valor de retorno no es iterable y eso es porque greeting se está utilizando como un componente
00:03:56de servidor. No podemos usar esta funcionalidad de cliente. Para solucionar esto, tienes dos opciones y la segunda
00:04:01opción es definitivamente la mejor a usar, pero la primera opción les resultará familiar a aquellos
00:04:05de ustedes que han usado Next.js. Podemos simplemente mover esa lógica a su propio componente y luego usar la
00:04:10directiva use client. Esto funciona en los componentes de servidor de TanStack Start, podemos simplemente usar el
00:04:14componente dentro del componente de servidor ahora y, como pueden ver, todo funciona bien. La desventaja
00:04:18de este enfoque, sin embargo, es que ahora tenemos un componente de servidor controlando la renderización de un
00:04:22componente de cliente y esto puede empezar a ser un poco desordenado, es decir, si quisiera averiguar dónde estaba mi
00:04:28componente de contador en el árbol, iría a mi ruta aquí y vería que tenemos un componente de servidor greeting,
00:04:32luego volvería al componente de servidor greeting y dentro del componente de servidor vería
00:04:37que tenemos un componente de cliente y este desorden realmente puede empezar a acumularse y simplemente hacer que ese límite
00:04:42entre servidor y cliente se vuelva un poco confuso. TanStack no se conformaría con eso, sin embargo. Se
00:04:47preguntaron: ¿qué pasa si el servidor no necesita decidir ninguna parte de la UI con forma de cliente en absoluto?
00:04:51Y esto los llevó a crear algo completamente nuevo: componentes compuestos (composite components). Para usar uno, lo primero
00:04:56que haré es eliminar nuestro componente de cliente de nuestro componente de servidor y simplemente lo reemplazaré
00:05:00con cualquier hijo (children) de nuestro componente greeting. A continuación, también necesitamos cambiar lo que estamos devolviendo de
00:05:05nuestra función de servidor aquí. En lugar de devolver un componente de servidor renderizable, necesitamos devolver
00:05:09lo que se llama una fuente compuesta (composite source). Para hacer eso, podemos usar nuestra segunda función de ayuda
00:05:14de componente de servidor de TanStack: create composite component. Aquí, esencialmente solo estamos construyendo un componente
00:05:18donde las props aquí se consideran las ranuras (slots). Solo estoy usando una ranura de children muy simple, así que
00:05:22va a pasar cualquier cosa que pongamos como hijo de mi componente compuesto a este props.children, que
00:05:27estoy pasando al componente greeting que acabamos de tener. Con eso, de nuevo, lo que necesitamos hacer es
00:05:31simplemente obtener nuestro componente compuesto de nuestra función de servidor y lo hacemos de la misma manera en
00:05:36el loader aquí. Verán que solo estoy renombrando source a greeting, luego lo cargo con use loader
00:05:41data. La única diferencia aquí, sin embargo, es que no podemos usar esto como un componente. Verán que está lanzando un
00:05:45error aquí. Para renderizar realmente un componente compuesto, necesitamos usar el componente de ayuda
00:05:49CompositeComponent que obtenemos de los componentes de servidor de TanStack y, como prop source, pasamos
00:05:53el componente compuesto que obtenemos de nuestra función de servidor que creamos anteriormente.
00:05:57Con eso, mi componente de servidor ahora se está renderizando como lo teníamos antes y también estoy pasando
00:06:01el contador como hijos de este componente compuesto y eso se está pasando al greeting donde
00:06:05lo teníamos configurado aquí, así que lo está pasando a props.children, por lo que todo funciona ahora
00:06:10muy bien. También puedo ir a mi componente de contador y eliminar la directiva que teníamos aquí, ya que eso ya
00:06:14no es necesario porque sabe que este va a ser un componente de cliente ya que está dentro de un componente
00:06:18compuesto. Esto se está utilizando como una ranura. Ahora, podría parecer que obtuvimos el mismo resultado
00:06:23pero con más trabajo que solo usar la directiva use client, pero el poder realmente proviene de la
00:06:27experiencia de desarrollo y es un poco un cambio respecto al modelo use client. En lugar de tener nuestro
00:06:32servidor decidiendo dónde se renderizan nuestros componentes de cliente, como cuando teníamos el componente de contador dentro
00:06:36del propio componente de servidor, en cambio, lo que estamos haciendo con los componentes compuestos es decir: oye,
00:06:40va a haber una ranura aquí, vamos a renderizar un componente de cliente, pero el
00:06:44propio componente de servidor no tiene idea de qué será eso. Lo añadimos más tarde en nuestro código de cliente,
00:06:48así que manejamos todos los componentes basados en el cliente dentro del propio código de cliente. Eso es solo el comienzo también
00:06:53si echamos un vistazo a una página más compleja como esta, la de una publicación, donde esta publicación está siendo renderizada en el servidor,
00:06:58hay dos problemas que quiero resolver. El primero es que quiero añadir algunas acciones como
00:07:03darle me gusta a la publicación y seguir al autor, pero quiero añadirlas encima del título aquí y quiero
00:07:08usar un componente de cliente. En este momento simplemente estoy usando este patrón de ranura de hijos (children slot), lo que significa que si
00:07:12añadiera mis acciones de publicación aquí abajo, simplemente irían donde están los comentarios, ya que así es como tenemos
00:07:17configurado el componente, así que quiero alguna forma de decirle a mi componente de servidor dónde poner componentes de cliente
00:07:22específicos. Luego tenemos un segundo problema, que es si tengo un botón de seguir autor. En este momento, esta
00:07:27página de publicación realmente no tiene idea de quién es el autor. De hecho, descargamos toda esa lógica en
00:07:32el propio componente de servidor. Si quisiera obtener el autor en un componente de cliente aquí abajo, tendría
00:07:37que obtener el JSON de la publicación y obtener el autor de esa manera, y ese no es realmente un gran
00:07:42patrón, estaríamos haciendo una doble solicitud de datos. Por suerte para nosotros, TanStack realmente tiene otros dos tipos de ranuras
00:07:46que podemos usar en un componente compuesto además de esta de children, y la primera va a
00:07:50ser render props. Esto es esencialmente cualquier prop que sea una función que devuelve un elemento de React, así que
00:07:56esto puede llamarse como sea, no tiene que llamarse render actions, y aquí solo estoy diciendo qué
00:07:59datos quiero que pase el componente de servidor y eso será el id de la publicación y el id del autor.
00:08:04Ahora todo lo que necesitamos hacer en nuestro componente compuesto es simplemente usar esta función que estamos pasando
00:08:08como prop donde quieras que esté el componente que eventualmente se va a renderizar.
00:08:12En mi caso, quiero que sea debajo del encabezado de la tarjeta, así que puedo llamarlo con props.render
00:08:16actions, podemos usar un opcional, así que si no se pasa, no se rompe, simplemente no se
00:08:20renderizará, luego también podemos pasar la información que queremos del componente de servidor
00:08:24a nuestro componente de cliente. Después de esto, nuestro componente compuesto va a aceptar la prop render actions
00:08:28que acabamos de crear y para el valor simplemente pasamos una función que tiene un id de publicación
00:08:32y un id de autor como argumento, que el servidor va a rellenar, luego simplemente renderizamos nuestro
00:08:36componente de cliente post actions y podemos pasar esos datos a través de las props. Así que ahora tengo un botón
00:08:41aquí arriba donde puedo dar me gusta y copiar el enlace a la publicación y también hacer clic en seguir autor aquí, donde
00:08:45es consciente del nombre del autor, a pesar de que en realidad nunca lo he obtenido en esta página.
00:08:49Solo lo obtengo en el componente de servidor y el componente de servidor está pasando esos datos al
00:08:53componente de cliente por mí. Ahora, podrías pensar que esto rompe la lógica que teníamos antes, donde dijimos que
00:08:57no queremos que ningún componente de servidor esté a cargo de renderizar los de cliente, pero no lo hace, y eso es
00:09:01porque las ranuras (slots) son en realidad opacas. El componente de servidor aquí arriba no tiene idea de qué hay dentro de
00:09:06esto, solo sabe que algo va aquí y que necesita pasar estos valores, que en
00:09:10este caso es el id de la publicación y el id del autor. Esta función no se ejecuta en el servidor, en cambio, el
00:09:15servidor simplemente ve que necesita pasar datos y luego, en nuestro cliente, es cuando
00:09:19la función se ejecuta realmente y el componente se renderiza. Exactamente lo mismo se aplica a
00:09:23nuestro tercer tipo de ranura, que es component props, esta es en realidad un poco más simple que las
00:09:28render props, todo lo que estamos haciendo es, en lugar de tener una función que luego devuelve nuestro componente de cliente,
00:09:33simplemente pasamos el componente de cliente como una prop en sí misma, luego en nuestro componente compuesto
00:09:38en la definición aquí arriba estamos diciendo que queremos aceptar una prop que sea un componente de React que tenga
00:09:42las props de id de publicación e id de autor, luego podemos usar esto dentro del componente mismo. Puedes pensar en
00:09:47component props como un marcador de posición, el componente de servidor sabe que va a haber un componente
00:09:51ahí que necesita algunos datos, en nuestro caso el id de la publicación y el id del autor, pero realmente no le importa qué es ese
00:09:56componente, siempre y cuando acepte esas props, así que cambié mi componente post actions aquí abajo a
00:10:01otro que hice llamado fake post actions y luego guardamos eso, pueden ver que esto
00:10:05todavía se va a renderizar porque es el cliente el responsable de renderizar este componente,
00:10:10es solo el servidor el que proporciona los datos, mirando la documentación no parece que haya
00:10:14ninguna diferencia real en qué enfoque tomes, ya sea que vayas con component props o render props,
00:10:18podría reducirse solo a preferencia, la única diferencia que puedo ver es que tal vez quieras
00:10:22modificar los datos que obtienes del servidor, así que en este caso podemos hacer lo que queramos con
00:10:26el id de la publicación y el id del autor, ya que es solo una función y luego podemos pasar eso a nuestro componente
00:10:31mientras que si estás usando component props, simplemente pasas el componente mismo y el servidor
00:10:36maneja el paso de las props, ahora eso es lo básico de los componentes de servidor de TanStack, pero hay
00:10:40todavía mucho más para disfrutar, por ejemplo, si quisieras que la mayor parte de tu página fuera renderizada en el servidor,
00:10:44tal vez tienes un componente de encabezado, un componente de contenido y uno de pie de página y los quieres todos renderizados en el servidor,
00:10:49no tienes que agruparlos todos en una sola función render server component, en realidad puedes
00:10:53usar Promise.all, dividirlos en tres funciones diferentes y luego simplemente devolverlos como un objeto
00:10:58desde una única función de servidor, pero ¿qué pasa si uno de esos componentes tarda mucho en cargar? Eso significaría
00:11:03que toda la función de servidor y, por lo tanto, toda la página lo haría, bueno, no se preocupen ahí tampoco,
00:11:07lo que realmente podemos hacer es, en lugar de esperar la función render server component, podemos
00:11:12devolver la promesa que crea y luego en el cliente podemos aprovechar el hook use y
00:11:16los límites de Suspense para cargar esqueletos, así que los componentes de servidor simplemente cargarán cuando estén listos.
00:11:21Realmente me gusta el enfoque que TanStack ha tomado aquí, no se siente intrusivo, no me fuerzan
00:11:25a adoptarlo y puedo adoptarlo sin ninguna solución alternativa extraña, además cuando realmente voy a usarlo
00:11:31los componentes de servidor en sí mismos son en realidad solo tres funciones nuevas, el resto es solo simple
00:11:36funciones de servidor de TanStack Start, algo que ya estaba usando, y es tan simple como obtener
00:11:41datos. Esto también significa que se integra muy bien con herramientas como TanStack Query, algo que definitivamente
00:11:45haré, y también hace que cosas como el almacenamiento en caché sean más simples, si quisieras podrías literalmente
00:11:49simplemente guardar en caché la respuesta de la solicitud GET en tu CDN, definitivamente voy a explorar esto más,
00:11:54así que háganme saber en los comentarios de abajo qué piensan de ellos y si les gustaría ver más videos sobre ellos
00:11:59bueno, sí, suscríbanse y como siempre, nos vemos en el próximo.