Contenido
Obtención de datos
Las props del servidor son el único canal de datos que ofrece Carats. Un culet obtiene datos en el servidor y un componente bruñido recibe el resultado.
Ciclo de vida del culet
El servidor resuelve el culet de la ruta solicitada antes de que se ejecute el componente de página, así que el componente siempre se renderiza con datos completos y no hay ningún estado de carga que gestionar. Cuando un culet lanza una excepción, el error se pasa al componente de suspensión error.
// src/server/culets/trade.ts
import { culet } from '@carats/ssr';
export default culet<TradeData>('/trade/:symbol', async (req) => {
const symbol = req.params.symbol;
if (!symbol) {
throw new Error('Symbol is required');
}
const res = await fetch(`https://api.example.com/quote/${symbol}`);
if (!res.ok) {
throw new Error(`Upstream quote failed with ${res.status}`);
}
return res.json();
});
Un error lanzado, venga del culet o del componente, llega al componente de suspensión error como un Error. Una URL sin coincidencia llega a notFound.
Forma de la petición
El manejador de un culet recibe todo lo que la petición llevaba: params para los parámetros de ruta, además de url, query, headers, cookies y method. No hay propiedad data, porque a los culets solo se llega con GET.
import { culet } from '@carats/ssr';
culet<Dashboard>('/dashboard', (req) => {
return {
// Route parameters
// req.params
// Query string: req.query
// Cookies: req.cookies
// Headers: req.headers
// req.url, req.method
theme: req.cookies.theme ?? 'dark',
tab: req.query.tab ?? 'overview',
};
});
Como cookies y headers están disponibles, un culet puede personalizar una página por visitante sin ninguna infraestructura extra. Aun así la respuesta se cachea por ruta, así que no pongas datos por visitante en un culet cuyo componente consumidor no esté marcado con recast.
Manejadores asíncronos
Un culet puede ser async, y esperar dentro de él es la forma normal de hablar con una base de datos o una API remota. Devolver una promesa es suficiente; no hace falta esperarla en el punto de llamada.
culet<TradeData>('/trade/:symbol', async (req) => {
const [quote, book] = await Promise.all([
fetchQuote(req.params.symbol),
fetchOrderBook(req.params.symbol)
]);
return { ...quote, orderBook: book };
});
Reglas de comparación de rutas
Un culet se vincula a su página únicamente por la cadena de ruta. La ruta del culet y la de la faceta deben ser idénticas carácter a carácter, incluidos los nombres de los parámetros; de lo contrario el componente se renderiza sin datos. Una ruta sin culet simplemente se renderiza sin props, que es el caso normal de las páginas estáticas.
// The facet route
'/trade/:symbol'
// The culet route — must be identical, including the parameter name
culet<TradeData>('/trade/:symbol', handler)
No hay comparación implícita por nombre de archivo ni por nombre de componente. Una errata en la cadena de ruta falla en silencio: la página se renderiza, las props quedan undefined y el error aflora como un fallo dentro del componente. Registrar la ruta del culet como una constante con nombre e importarla en ambos sitios elimina ese modo de fallo.
Caché y recast
El framework conserva las props del servidor más recientes de una ruta y las reutiliza, así que un componente bruñido no vuelve a obtener datos en cada renderizado. Cuando una página deba mostrar siempre datos frescos, prescinde de la caché por componente con la opción recast.
Forzar una nueva obtención
Envuelve el componente con { recast: true } para saltarte la caché en cada renderizado. Se aplica solo a ese componente, lo que lo hace una buena opción para páginas como un panel en vivo en lugar de un valor por defecto para todo el sitio.
import { Burnish } from '@carats/render';
// Cached: reuses the most recent server props for the route
export default Burnish<TradeData>(function (data) {
return <h1>{data.symbol} {data.price}</h1>;
});
import { Burnish } from '@carats/render';
// Not cached: refetches on every render
export default Burnish<TradeData>(function (data) {
return <h1>{data.symbol} {data.price}</h1>;
}, { recast: true });
recast pertenece al componente, no al culet, así que una sola página en vivo no obliga a todos los consumidores de esa ruta a volver a obtener datos.
El objeto global de props del servidor
Las props serializadas se exponen en window.carats.ssp. ssp.for es la ruta a la que pertenecen y ssp.data es el valor. Gracias a esto un componente no bruñido, o el código normal de una llamada afterMount, puede leer datos que el servidor ya obtuvo, en lugar de lanzar una segunda petición.
import { afterMount } from '@carats/hooks';
export default function LiveBadge() {
afterMount(() => {
// Read the props the server already resolved for this route
const ssp = window.carats.ssp;
const el = document.getElementById('live-badge');
if (!el || !ssp) return;
el.textContent = `${ssp.for}: ${JSON.stringify(ssp.data)}`;
});
return <span id="live-badge"></span>;
}
El objeto global del cliente está tipado, así que window.carats no necesita ninguna conversión. ssp.for es la ruta a la que pertenece la carga útil, lo que permite a un componente compartido comprobar que los datos que va a leer se resolvieron para la página en la que se renderiza, y saltárselos cuando no fue así.