Saltar al contenido principal
nota

El original en inglés cambió después de esta traducción, así que puede estar desactualizada.

Política de caducidad de Endpoint

Por defecto, la política de caché de Reactive Data Client se puede describir como stale-while-revalidate. Esto significa que, cuando hay datos disponibles, puede evitar bloquear la aplicación usando los datos obsoletos. Sin embargo, en segundo plano seguirá actualizando los datos si son lo bastante antiguos.

Estado de caducidad​

dentro de dataExpiryLengthCuánto tiempo (en milisegundos) una respuesta se mantiene fresca antes de volverse obsoleta. Más información
useSuspense()GET

Fresh​

Los datos en este estado se consideran lo bastante recientes como para no necesitar un fetch.

Si está activado, los datos se saltan Stale y pasan a ser inválidos en cuanto caducan, así que deben volver a obtenerse antes de poder renderizarse. Más información
después de dataExpiryLengthUna vez pasado dataExpiryLength (en milisegundos), la respuesta en caché está obsoleta: se sigue mostrando, pero se vuelve a obtener. Más información
useSuspense()GET

Stale​

Los datos todavía se pueden mostrar, pero Reactive Data Client podría intentar revalidarlos haciendo un nuevo fetch.

después de ctrl.invalidate()Marca los datos como inválidos de inmediato, incluso si siguen frescos, para que los componentes que los necesiten vuelvan a obtenerlos. Más información
useSuspense()GET

Invalid​

Los datos no deben mostrarse. Cualquier componente que necesite estos datos desencadenará un fetch y suspense. Si ningún componente necesita estos datos, no se realizará ninguna acción.

useSuspense() considera hacer fetch al montarse y también cuando cambian sus parámetros. En estos casos hará fetch si los datos se consideran obsoletos.

React Native

Al usar React Navigation, los eventos de foco también desencadenan fetch de los datos obsoletos.

Tiempo de caducidad​

Endpoint.dataExpiryLength​

Endpoint.dataExpiryLength establece cuánto tiempo (en milisegundos) tardan los datos en pasar del estado 'fresh' al estado 'stale'. Prueba a establecerlo en un número muy bajo, como '50', para que pasen a stale casi al instante; o en un número muy grande para que se mantengan durante mucho tiempo.

Alternar entre 'first' y 'second' cambia los parámetros. Si los datos todavía se consideran fresh, seguirás viendo la hora anterior sin ninguna actualización.

Fixtures
▶api/lastUpdated
▶getUpdated
import { lastUpdated } from './api/lastUpdated';

export const getUpdated = lastUpdated.extend({ dataExpiryLength: 10000 });
▶TimePage
import { useSuspense } from '@data-client/react';
import { getUpdated } from './getUpdated';

export default function TimePage({ id }) {
  const { updatedAt } = useSuspense(getUpdated, { id });
  return (
    <div>
      API time for {id}:{' '}
      <time>
        {updatedAt.toLocaleString('en-US', { timeStyle: 'long' })}
      </time>
    </div>
  );
}
▶Navigator
Resultado
Store▶
@data-client/rest

Tiempo de vida largo en la caché

LongLivingResource.ts
import {
RestEndpoint,
RestGenerics,
resource,
} from '@data-client/rest';

// We can now use LongLivingEndpoint to create endpoints that will be cached for one hour
class LongLivingEndpoint<
O extends RestGenerics,
> extends RestEndpoint<O> {
dataExpiryLength = 60 * 60 * 1000; // one hour
}

const LongLivingResource = resource({
path: '/:id',
Endpoint: LongLivingEndpoint,
});

Nunca reintentar en caso de error

NoRetryResource.ts
import {
RestEndpoint,
RestGenerics,
resource,
} from '@data-client/rest';

// We can now use NoRetryEndpoint to create endpoints that will be cached for one hour
class NoRetryEndpoint<
O extends RestGenerics,
> extends RestEndpoint<O> {
errorExpiryLength = Infinity;
}

const NoRetryResource = resource({
path: '/:id',
Endpoint: NoRetryEndpoint,
});

Endpoint.invalidIfStale​

Endpoint.invalidIfStale elimina el estado 'stale', de modo que los datos que caducan se consideran 'invalid' de inmediato.

Esto se demuestra porque el componente se suspende en cuanto sus datos pasan a stale. Si los datos aún están dentro del tiempo de caducidad, simplemente sigue mostrándolos.

Fixtures
▶api/lastUpdated
▶getUpdated
import { lastUpdated } from './api/lastUpdated';

export const getUpdated = lastUpdated.extend({
  invalidIfStale: true,
  dataExpiryLength: 5000,
});
▶TimePage
import { useSuspense } from '@data-client/react';
import { getUpdated } from './getUpdated';

export default function TimePage({ id }) {
  const { updatedAt } = useSuspense(getUpdated, { id });
  return (
    <div>
      API time for {id}:{' '}
      <time>
        {updatedAt.toLocaleString('en-US', { timeStyle: 'long' })}
      </time>
    </div>
  );
}
▶Navigator
Resultado
Store▶

Forzar la actualización​

A veces queremos obtener datos nuevos mientras seguimos mostrando los datos antiguos (obsoletos).

Un endpoint específico​

Controller.fetch se puede usar para desencadenar un fetch mientras se siguen mostrando los datos anteriores. Esto se puede hacer incluso con datos 'fresh'.

Fixtures
▶api/lastUpdated
▶ShowTime
import { useSuspense, useController } from '@data-client/react';
import { lastUpdated } from './api/lastUpdated';

function ShowTime() {
  const { updatedAt } = useSuspense(lastUpdated, { id: '1' });
  const ctrl = useController();
  return (
    <div>
      <time>
        {updatedAt.toLocaleString('en-US', { timeStyle: 'long' })}
      </time>{' '}
      <button onClick={() => ctrl.fetch(lastUpdated, { id: '1' })}>
        Refresh
      </button>
    </div>
  );
}
render(<ShowTime />);
Resultado
Store▶

Actualizar los endpoints visibles​

Controller.expireAll() establece en Stale el estado de caducidad de todas las respuestas que coincidan con testKey.

Fixtures
▶api/lastUpdated
▶ShowTime
▶Loading
▶Demo
import { AsyncBoundary, useController } from '@data-client/react';

import { lastUpdated } from './api/lastUpdated';
import ShowTime from './ShowTime';
import Loading from './Loading';

function Demo() {
  const ctrl = useController();
  return (
    <div>
      <AsyncBoundary fallback={<Loading id="1" />}>
        <ShowTime id="1" />
      </AsyncBoundary>
      <AsyncBoundary fallback={<Loading id="2" />}>
        <ShowTime id="2" />
      </AsyncBoundary>
      <AsyncBoundary fallback={<Loading id="3" />}>
        <ShowTime id="3" />
      </AsyncBoundary>

      <button onClick={() => ctrl.expireAll(lastUpdated)}>
        Expire All
      </button>
      <button onClick={() => ctrl.fetch(lastUpdated, { id: '1' })}>
        Force Refresh First
      </button>
    </div>
  );
}
render(<Demo />);
Resultado
Store▶

Invalidar (volver a suspender)​

Tanto los endpoints como las entities pueden invalidarse.

Un endpoint específico​

En este ejemplo, invalidar el endpoint muestra el fallback de carga, ya que no se permite mostrar los datos.

Fixtures
▶api/lastUpdated
▶ShowTime
▶Loading
▶Demo
import { AsyncBoundary, useController } from '@data-client/react';

import { lastUpdated } from './api/lastUpdated';
import ShowTime from './ShowTime';
import Loading from './Loading';

function Demo() {
  const ctrl = useController();
  return (
    <div>
      <AsyncBoundary fallback={<Loading id="1" />}>
        <ShowTime id="1" />
      </AsyncBoundary>
      <AsyncBoundary fallback={<Loading id="2" />}>
        <ShowTime id="2" />
      </AsyncBoundary>
      <AsyncBoundary fallback={<Loading id="3" />}>
        <ShowTime id="3" />
      </AsyncBoundary>

      <button onClick={() => ctrl.invalidateAll(lastUpdated)}>
        Invalidate All
      </button>
      <button
        onClick={() => ctrl.invalidate(lastUpdated, { id: '1' })}
      >
        Invalidate First
      </button>
    </div>
  );
}
render(<Demo />);
Resultado
Store▶

Cualquier endpoint con una entity​

Usar el schema Invalidate nos permite invalidar cualquier endpoint que dependa de esa entity en su respuesta. Si el endpoint usa la entity dentro de un Array, simplemente se eliminará de ese Array.

Fixtures
▶api/lastUpdated
▶TimePage
▶ShowTime
import { Invalidate, RestEndpoint } from '@data-client/rest';
import { AsyncBoundary, useController, useLoading } from '@data-client/react';
import { TimedEntity } from './api/lastUpdated';
import TimePage from './TimePage';

const InvalidateTimedEntity = new Invalidate(TimedEntity);
export const deleteLastUpdated = new RestEndpoint({
  path: '/api/currentTime/:id',
  method: 'DELETE',
  schema: InvalidateTimedEntity,
});

function ShowTime() {
  const ctrl = useController();
  const [handleDelete, loadingDelete] = useLoading(
    () => ctrl.fetch(deleteLastUpdated, { id: '1' }),
    [],
  );
  return (
    <div>
      <AsyncBoundary fallback={<div>loading...</div>}>
        <TimePage id="1" />
      </AsyncBoundary>
      <div>
        Current Time: <CurrentTime />
      </div>
      <button onClick={handleDelete}>
        {loadingDelete ? 'loading...' : 'Invalidate'}
      </button>
      <button
        onClick={() =>
          ctrl.setResponse(
            deleteLastUpdated,
            { id: '1' },
            { id: '1' },
          )
        }
      >
        Invalidate (without fetching DELETE)
      </button>
      <button
        onClick={() => ctrl.set([InvalidateTimedEntity], [{ id: '1' }])}
      >
        Invalidate Entity with ctrl.set
      </button>
    </div>
  );
}
render(<ShowTime />);
Resultado
Store▶

Controller.fetch() nos permite actualizar el servidor y el store. Podemos usar Controller.setResponse() o Controller.set() cuando queremos modificar directamente el store local.

Invalidación condicional según los datos​

Si la invalidation debe ocurrir solo a veces, según los datos de la respuesta, podemos devolver undefined desde Entity.process.

class PriceLevel extends Entity {
price = 0;
amount = 0;

pk() {
return this.price;
}

static process(
input: [number, number],
parent: any,
key: string | undefined,
): any {
const [price, amount] = input;
if (amount === 0) return undefined;
return { price, amount };
}
}