Saltar al contenido

Revalidación: invalidar la caché tras mutar

Tras una mutación, volver a pedir solo lo necesario. Por qué una `action` revalida todas las queries por defecto, cómo acotar la revalidación a una clave concreta con `json` y la opción `revalidate`, cómo `getX.key` y `getX.keyFor` distinguen entre invalidar todas las instancias de una query o una sola, cómo `revalidate` imperativo fuerza el refetch fuera de una acción, y cómo `createAsyncStore` con `reconcile` hace que la revalidación actualice de grano fino en vez de remontar.

La revalidación automática de las acciones

El comportamiento por defecto es deliberadamente conservador: cuando una action termina con éxito, el router revalida todas las queries activas. La lógica es de seguridad —el framework no puede saber qué datos dependían de lo que mutaste, así que asume que cualquiera pudo quedar obsoleto y los recomputa todos—. Es correcto siempre, pero pide de más: recarga queries que la mutación no rozó.

TSX
// Sin acotar: al crear un todo, TODAS las queries se revalidanexport const addTodo = action(async (texto: string) => {  await fetch("/api/todos", { method: "POST", body: JSON.stringify({ texto }) });  // ...sin mas, el router revalida getTodos, getUsuario, getFacturas...}, "addTodo");

Para una app pequeña esto basta y es lo más simple que funciona. En cuanto la superficie de datos crece, revalidar todo tras cada mutación se nota, y conviene decirle al router exactamente qué recomputar.

Invalidar solo lo necesario

La forma declarativa de acotar es devolver el resultado con json y su opción revalidate, indicando qué claves recomputar. Solo esas queries se refrescan; el resto conserva su valor cacheado.

TSX
import { action, json } from "@solidjs/router";import { getTodos } from "~/data/todos";export const addTodo = action(async (texto: string) => {  const nuevo = await crearEnServidor(texto);  return json(nuevo, { revalidate: getTodos.key }); // solo la lista de todos}, "addTodo");

Aquí aparece la distinción crucial entre las dos claves que ya conociste. getTodos.key es el nombre base: revalidarlo recomputa todas las instancias de esa query, sin importar sus argumentos. getUsuario.keyFor("7") es la clave de una instancia concreta: revalidarlo recomputa solo el usuario 7 y deja intactos los demás usuarios cacheados.

TSX
revalidate: getUsuario.keyFor(id);   // solo ese usuariorevalidate: getUsuario.key;          // todos los usuariosrevalidate: [getTodos.key, getUsuario.keyFor(id)]; // varias claves

La misma opción revalidate viaja en los otros dos resultados que una acción puede devolver. reload reejecuta la ruta actual acotando qué recomputar, y redirect navega a otra ruta arrastrando consigo qué claves refrescar en el destino. Así, la decisión de qué queda obsoleto acompaña a la acción sea cual sea su desenlace —quedarse, recargar o redirigir—.

TSX
import { action, redirect, reload } from "@solidjs/router";export const borrarTodo = action(async (id: string) => {  await borrarEnServidor(id);  return reload({ revalidate: getTodos.key }); // recarga la ruta, solo la lista}, "borrarTodo");export const crearFactura = action(async (datos: DatosFactura) => {  const nueva = await crearEnServidor(datos);  throw redirect(`/facturas/${nueva.id}`, { revalidate: getFacturas.key });}, "crearFactura");

revalidate imperativo

No toda revalidación nace de una acción. A veces quieres refrescar tras un evento externo —un websocket que avisa de un cambio, un botón de “actualizar”, un temporizador—. Para eso está la función revalidate, que dispara el refetch de una o varias claves desde cualquier sitio.

TSX
import { revalidate } from "@solidjs/router";await revalidate(getUsuario.keyFor(id)); // refresca un usuarioawait revalidate(getTodos.key);          // refresca toda una queryawait revalidate();                       // refresca absolutamente todo

revalidate devuelve una promesa que resuelve cuando el refetch termina, así que puedes esperar a que los datos frescos estén listos antes de, por ejemplo, cerrar un modal. Es la misma maquinaria que usan las acciones por dentro, ahora a tu disposición.

Revalidar sin remontar: createAsyncStore

Revalidar trae la colección entera de nuevo, y si la lees con createAsync, su valor es atómico: cada refetch reemplaza el objeto completo y un For sobre él remonta todas las filas, aunque solo cambiara una. createAsyncStore resuelve esto: guarda el resultado en un store y aplica reconcile en cada revalidación, cosiendo la respuesta nueva sobre la vieja y preservando la identidad de lo que no cambió.

TSX
import { createAsyncStore } from "@solidjs/router";// Cada revalidacion difea contra el valor previo por la clave id:const todos = createAsyncStore(() => getTodos(), {  reconcile: { key: "id" },});// <For each={todos()}> solo remonta las filas que de verdad cambiaron

Con esto, la lección anterior y esta encajan: la mutación optimista pinta el futuro al instante, la revalidación trae la verdad del servidor, y reconcile hace que esa verdad aterrice de grano fino —la fila fantasma cede su sitio a la real sin que el resto de la lista parpadee—.