Herramientas de usuario

Herramientas del sitio


informatica:programacion:cursos:desarrollo_web_react_18:comunicacion_interaccion_servidor

¡Esta es una revisión vieja del documento!


Comunicación e interacción con el servidor

Notas del curso Desarrollo de aplicaciones web con React 18

Una interfaz bonita y altamente interactiva no sirve de nada si no puede cargar y guardar datos en un lugar más permanente. En el caso de una aplicación web, ese lugar sería un servidor o, más concretamente, el backend de la aplicación web.

En el caso de una aplicación web totalmente independiente y sin conexión a ningún servidor, podríamos utilizar el localStorage del navegador como almacenamiento persistente. Su uso es muy simple y la metodología para integrar la carga y actualización de los datos en React/Redux sería muy similar a como lo haremos con un servidor. Aunque, claro, solo se mantendrían los datos en ese mismo navegador y sería conveniente facilitar una manera de exportarlos e importarlos con facilidad.

A lo largo de este módulo, aprenderás cómo establecer una comunicación entre la aplicación y un servidor que presente una API REST. Esto lo haremos tanto con React, para aplicaciones simples, como utilizando Redux Toolkit, para aplicaciones más complejas como nuestro gestor de tareas kanban. Además, nos familiarizaremos con otras técnicas que harán la experiencia de usuario de nuestra aplicación más amigable y completa.

Nuevamente, muchos de los conceptos y técnicas que vas a aprender en este módulo pueden resultarte algo complejos. No te preocupes, es normal. Redux es complicado. Procura ir adelante y atrás para repasar conceptos, comparar el código antes y después de los cambios, probar a crearlo por tu cuenta, etc. En definitiva, invertir tiempo en practicar, que es la única forma de asentar conocimientos. Y para cualquier duda que te surja tras haber hecho todo esto, contacta con el tutor en el foro (para que otros puedan sacar partido a las respuestas y participar) o por mensajería interna (si es una duda que no aporta a los demás o si es sobre una práctica).

Comunicación directa con la API - Ejemplo microblog

Puesto que React es únicamente una biblioteca para la creación de interfaces de usuario interactivas, en principio no impone ninguna restricción acerca de la metodología que debemos usar para cargar y enviar datos a fuentes externas. El único conflicto que podemos llegar a tener es el hecho de que las funciones que escribimos al construir los componentes deben ser puras y no pueden tener efectos secundarios. En particular, realizar peticiones a servicios externos es un efecto secundario.

En esta lección aprenderemos a indicar mediante el hook useEffect los efectos secundarios que queremos que ocurran en el contexto de un componente, de forma que React pueda controlar esos efectos y actualizar la interfaz convenientemente.

Con el objetivo de desarrollar un ejemplo autocontenido, vamos a construir un sistema de microblogging para un único usuario, con capacidad para cargar, editar y enviar publicaciones. Para ello, puedes crear un nuevo proyecto React con npm init vite y eliminar todos los archivos del directorio src salvo App.jsx, index.css y main.jsx.

Simulación de la API REST con json-server

Antes de comenzar a implementar esta aplicación, vamos a definir la fuente de datos que va a consumir. La biblioteca json-server nos aporta funcionalidad de sobra para este propósito, ya que es capaz de simular una API REST a partir de un archivo JSON de datos, e incluso permite algunas peticiones relativamente complejas para realizar diferentes búsquedas y consultas.

Para disponer de un servidor con esta biblioteca, puedes utilizar el archivo db.json que encontrarás en los archivos asociados a la lección, y del que se muestra un extracto a continuación:

{
  "cuentas": [
    {
      "nombre": "Miguel",
      "id": 1
    }
  ],
  "posts": [
    {
      "contenido": "En un lugar de la Mancha, [...] partes de su hacienda.",
      "autoria": 1,
      "id": 12,
      "fecha": "1605-01-01T12:00:00Z"
    },
    {
      "contenido": "El resto della [...] tomaba la podadera.",
      "autoria": 1,
      "id": 13,
      "fecha": "1605-01-02T12:00:00Z"
    },
    {
      "contenido": "Frisaba la edad [...] de la caza.",
      "autoria": 1,
      "id": 14,
      "fecha": "1605-01-03T12:00:00Z"
    }
  ]
}

Es importante que entiendas qué entidades de datos vamos a manejar y cómo se relacionan entre ellas, así que antes de continuar, dedica un tiempo a analizar el archivo db.json y a comprender su estructura.

Para poder ejecutar el servidor, es necesario instalar json-server como dependencia e invocarlo desde la terminal con el comando json-server db.json. Es muy posible que el ejecutable no se encuentre en el PATH, así que lo más cómodo será añadir un script al archivo package.json de nuestra aplicación indicando:

"server": "json-server --watch db.json"

De esta forma, podremos ejecutar npm run server y el gestor de paquetes se encargará de buscar el ejecutable de json-server que necesita para arrancar el servidor. Normalmente, este escuchará por defecto en el puerto 3000, pero se puede modificar si fuera necesario. En cualquier caso, ese puerto habrá que tenerlo en cuenta para que la aplicación React pueda enviar peticiones.

Obtención y manipulación de los datos

Como ya se ha mencionado, React no incorpora herramientas que sirvan expresamente para la interacción con fuentes externas. Por tanto, podemos implementar una interfaz para la API libremente. En este caso, puesto que se trata de un caso sencillo, vamos a recurrir directamente a las utilidades integradas en JavaScript: esencialmente, la función fetch y la gestión de promesas con then.

Para simplificar el código posterior, vamos a crear una función que construya el esqueleto que van a tener todas nuestras peticiones al servidor. Recibirá como parámetros un endpoint (es decir, la ruta a consultar tras la URL base de la API), un método HTTP opcional (por defecto GET) y un cuerpo de la petición opcional. Todas las peticiones se envían y reciben en formato JSON, por lo que hacemos uso de la cabecera HTTP correspondiente y las funcionalidades de JavaScript para convertir objetos a JSON y respuestas JSON a objetos.

const API_BASEURL = "http://localhost:3000"
const api = (endpoint, method = "GET", body) => {
  return fetch(API_BASEURL + endpoint, {
    method,
    headers: {
      'Content-Type': 'application/json'
    },
    body: body ? JSON.stringify(body) : null
  }).then(r => r.json())
}

Hay que notar que esta implementación no estaría completa en el caso de una aplicación para producción, ya que estamos haciendo caso omiso de los posibles errores que puedan producirse en las peticiones, y estamos asumiendo que todas se van a completar correctamente. Lo hacemos en pos de simplificar este ejemplo para centrarnos en lo importante para la formación: integrar la carga y envío de datos en React. Lo otro son cuestiones que cualquier desarrollador competente de JavaScript debe saber hacer.

Habrás notado que la función api no solo envía la petición, sino que devuelve el resultado de llamar a la función fetch(). Este es una promesa, lo cual nos permite encadenar acciones que se mantendrán a la espera de que se complete la petición, y se ejecutarán cuando esta termine. Por ejemplo, si obtenemos las publicaciones del blog, será interesante ordenarlas de forma que las más recientes aparezcan primero:

const getBlog = () => api("/posts").then(ps =>
  ps.sort((a, b) => Date.parse(b.fecha) - Date.parse(a.fecha))
)

De forma similar, definimos funciones que ejecuten llamadas concretas a la API: la consulta de una cuenta de autor, así como la edición, creación y borrado de una publicación:

const getAuthor = autorId => api(`/cuentas/${autorId}`)
const editPost = (id, contenido) => api(`/posts/${id}`, "PATCH", { contenido })
const saveNewPost = contenido => api("/posts", "POST", {
  contenido, autoria: 1, fecha: (new Date()).toISOString()
})
const deletePost = id => api(`/posts/${id}`, "DELETE")

Todas estas funciones nos permiten aislar los efectos secundarios que trataremos de que ocurran en la página web.

El hook useEffect

Dentro de la función que renderiza un componente, los efectos secundarios que queramos que tengan lugar independientemente de posibles eventos tendrán que hacerse a través de un hook denominado useEffect. Esto quiere decir que React ofrece dos ubicaciones para enviar y recibir datos externos:

  • Las funciones manejadoras de eventos
  • Las funciones englobadas en useEffect

Esto es así porque dichas funciones se ejecutan en un contexto diferente y no afectan de forma directa a la renderización del componente, que es lo que debe ser una operación pura.

El hook useEffect no devuelve nada y recibe dos parámetros:

  • setup: debe ser una función que implemente los efectos que deben ocurrir cada vez que se ejecute el efecto. Opcionalmente, puede devolver una función de limpieza que se ejecutará antes de volver a ejecutar el efecto.
  • dependencies: es un parámetro opcional que indica la lista de dependencias del efecto, es decir, qué datos son necesarios para computar dicho efecto de forma que cuando estos cambien, se vuelva a ejecutar de manera automática.

Un ejemplo simple podría ser una página de búsqueda en la que podemos filtrar las publicaciones por varios criterios, siendo uno de ellos el rango de fecha. El efecto principal sería cargar las publicaciones en ese rango y añadirlas a la interfaz. Cuando el intervalo de fechas elegido cambie, habría que limpiar la lista de publicaciones que se está mostrando y volver a cargarla otra vez con el nuevo rango:

useEffect(() => {
  getPublicacionesEntre(fechaInicio, fechaFin).then(
    res => agregarPublicaciones(res)
  )
  return () => {
    limpiarPublicaciones()
  }
}, [fechaInicio, fechaFin])

Utilizando el parámetro dependencies, los efectos se pueden configurar para que tengan lugar en tres casos posibles:

  • Una única vez en la primera renderización del componente: si indicamos un array vacío [].
  • Solamente cuando cambien datos concretos: si especificamos esos datos como un array de elementos, por ejemplo: [a, b]. Esto también ejecuta el efecto en la primera renderización.
  • En cada renderización del componente, si no indicamos el parámetro.

Los efectos que no deben ejecutarse en la primera renderización pero sí deben preceder a la siguiente invocación del efecto principal, irán en la función devuelta por la que pasemos como parámetro a useEffect.

Peticiones en aplicaciones con Redux: el asunc thunk

Comunicación mediante RTK Query - Aplicación al gestor Kanban

Estilos con Tailwind CSS

Componentes con ranuras para elementos

Composición de componentes y componentes de orden superior

Prácticas propuestas para el módulo

informatica/programacion/cursos/desarrollo_web_react_18/comunicacion_interaccion_servidor.1709299307.txt.gz · Última modificación: por tempwin