Saltar al contenido principal

10 publicaciones etiquetados con "Versiones"

Nuevas versiones de Reactive Data Client

Ver Todas las Etiquetas

v0.19: Coding Agents, Batch Controller.set(), Faster TypeScript

· 37 min de lectura
Nathaniel Tucker
Creador de Reactive Data Client

v0.19 makes coding agents a first-class way to build with Data Client: one prompt sets up your app, and your agent follows skills that ship with the packages you installed. It also lets a Manager write a whole batch of streamed rows in one store update, type-checks endpoint code about 2x faster, and fixes a round of TypeScript and Vue issues.

Coding Agents:

  • One-prompt setup - Add the data-client plugin to Claude Code, Cursor or Codex and ask it to set up Data Client; it installs the packages, adds the provider and links the skills your app needs
  • Skills ship in the packages - npx --no data-client-skills links the skills your installed @data-client/* packages carry, so your agent follows the version you have instead of an older release
  • Agents keep using Data Client - Setup adds a note to AGENTS.md, and each skill names the everyday tasks it covers, so later sessions reach for Data Client instead of fetch in useEffect
  • Debug the running app - In development, globalThis.__DC__ gives your agent the store's Controller and recent actions from Cursor's browser, Chrome DevTools MCP, Playwright MCP or Claude in Chrome
  • Vue skills, llms.txt, and skills generated from these docs

New APIs:

Performance:

Other Improvements:

  • Fix TypeScript 7 module resolution for package exports; imports now resolve to declaration files (#4019)
  • renderDataHook() runs provider mount effects when the first render suspends (#4099)
  • Controller.set() values are typed by the schema, so ctrl.set(new schema.All(Todo), 42) is a TypeScript error (#4133)
  • Controller.set() deletes one entity with an Invalidate schema without the one-row batch workaround (#4230)
  • process() gets typed params in new RestEndpoint(), .extend() and resource().extend(), so a wrong path parameter is a TypeScript error instead of undefined at runtime (#4183, #4184)
  • getOptimisticResponse() returns are typed by the schema, so a wrong field type is a TypeScript error instead of a flash of bad data (#4198)
  • Vue useSuspense() and useLive() keep the previous data while new arguments load, instead of returning undefined (#4131)
  • Vue useSuspense() and useLive() send fetch errors after arguments change to onErrorCaptured() instead of an unhandled promise rejection (#4135)
  • Vue useSuspense(), useDLE() and useFetch() no longer refetch stale data on every store update, so a controller.set() is not overwritten (#4134)
  • Vue useSuspense() shows stale data on mount and refetches in the background, like React, instead of showing the <Suspense> fallback until the refetch finishes (#4169)
  • Vue useDLE() and useCache() keep expired invalidIfStale data through unrelated store updates instead of getting stuck loading (#4142)
  • Vue DataClientPlugin installs on Vue versions before 3.5 instead of throwing app.onUnmount is not a function (#4146)
  • Vue DataClientPlugin garbage collects by default, matching React's DataProvider, so you no longer need to pass gcPolicy; how it decides what to remove (#4148)
  • Vue composables accept getter arguments like () => ({ id: props.id }); they were typed to allow them but passed the function itself to the endpoint (#4115)
  • Fix Cannot find name 'NoInfer' and export type errors on TypeScript 4.x with skipLibCheck off (#4138)
  • Fix Entity, Endpoint, Union and RestEndpoint type errors on TypeScript 4.0–4.5 with skipLibCheck off (#4140)
  • Fix ',' expected errors importing @data-client/react/redux, @data-client/react/nextjs or @data-client/test on TypeScript 4.0–4.4 (#4232)
  • Fix act and renderHook from @data-client/test typed as any on TypeScript 4.0–4.4 (#4251)
  • React Native useSuspense(), useFetch() and useDLE() refetch on screen focus only when the current data is stale or invalid, instead of refetching fresh data on every refocus (#4378)
  • renderDataHook() works with React Native Testing Library 14 and stays synchronous, so renderDataHook() calls need no await (#4374)
  • Entity classes can be used where an EntityInterface is expected (#4149); prepare pk() overrides for a future release
  • useCache() and useDLE() return undefined for a deleted entity whose refetch failed, instead of a truthy Symbol that slipped past if (!data) checks; remove any workarounds (#4150)
  • Vue useFetch() keeps its data from being garbage collected while mounted, like useSuspense(), so a configured gcPolicy no longer evicts prefetched data that components read later (#4152)
  • Apps with Redux DevTools open no longer stutter in development on large stores or frequent updates; each update serializes 40-60x faster (#4163)
  • resource().extend() with an object of endpoint overrides keeps endpoints added earlier with .extend('name', options) in its type, so using them is no longer a TypeScript error (#4184)
  • RestEndpoint.push, unshift and remove accept the Entity's fields as the body when a Query or Lazy wraps the Collection, instead of only FormData (#4226)
  • Vue useFetch() is typed as the read-only Ref it returns, so promise.resolved (always undefined) is now a TypeScript error; read promise.value.resolved instead (#4114)
  • A Controller subclass passed to DataProvider or DataClientPlugin no longer needs a cast to type-check (#4228)
  • prepareStore()'s store can be passed to react-redux's <Provider> without a cast, and has dispatch() in its type (#4238)
  • resource().create accepts FormData without a cast, like update, partialUpdate and getList.push (#4235)
  • RestEndpoint.push, unshift and remove send an array body as JSON, so creating or removing several items at once no longer sends "[object Object]" to your server; resource().create takes an array too (#4262)
  • Polling keeps the schema of endpoints given pollFrequency with .extend(), so a polled useLive() list updates right away from mutations and optimistic updates instead of waiting for the next poll (#4437)
  • DataProvider only warns that it doesn't update during SSR when something fetches while the server renders, instead of on every server render, so pages that load data after mount (like Expo Router's static web output) stay quiet (#4449)
  • Server rendering with React 19 no longer logs "Uncaught suspense" when nothing suspended; React renders and discards fallbacks of large boundaries, and the warning now waits for a fallback that actually shows (#4450)
  • Vue createDataClient() is deprecated, since it's internal to DataClientPlugin; install the plugin with app.use(DataClientPlugin, options) instead of calling it directly (#4193)
  • Vue renderDataCompose()'s waitForNextUpdate() is deprecated, since it gives up silently after 1 second and lets a test pass while still suspended; await result instead, or await allSettled() after a change, which now also waits for fetches a prop change starts (unit testing composables, #4221)

Breaking Changes:

Get ready for future releases:

v0.18: Scalar, descargas de archivos tipadas, Collections conscientes de filtros

· 13 min de lectura
Nathaniel Tucker
Creador de Reactive Data Client

v0.18 se centra en un modelado de datos más rico para valores que varían según el contexto de la petición, descargas tipadas más simples y una coincidencia de collections que refleja mejor los filtros reales de las APIs.

Nuevas funcionalidades:

Otras mejoras:

Fixtures
▶api/Company
import { Collection, Entity, RestEndpoint, Scalar } from '@data-client/rest';

export class Company extends Entity {
  id = '';
  name = '';
  price = 0;
  pct_equity = 0;
  shares = 0;
}

const PortfolioScalar = new Scalar({
  lens: args => args[0]?.portfolio,
  key: 'portfolio',
  entity: Company,
});
Company.schema = {
  pct_equity: PortfolioScalar,
  shares: PortfolioScalar,
};

export const getCompanies = new RestEndpoint({
  path: '/companies',
  searchParams: {} as { portfolio: string },
  // `portfolio` is a lens, not a filter — the returned Company list is the same
  // regardless of lens. Dropping it from `argsKey` collapses every portfolio to
  // one Collection pk, so `Collection.queryKey()` finds the list on every
  // switch and `useSuspense` reuses it without refetching.
  schema: new Collection([Company], { argsKey: () => ({}) }),
});

export const getPortfolioColumns = new RestEndpoint({
  path: '/companies/columns',
  searchParams: {} as { portfolio: string },
  schema: new Collection([PortfolioScalar], {
    argsKey: ({ portfolio }: { portfolio: string }) => ({ portfolio }),
  }),
});
▶CompanyGrid
▶PortfolioGrid
Vista previa en vivo
Store▶

El ejemplo muestra las mismas entidades Company renderizadas a través de distintos lenses de portafolio. Los campos estables de la Entity se reutilizan, mientras que los valores dependientes del lens se seleccionan de celdas Scalar separadas.

Cambios incompatibles:

¿Usas un agente de IA? Instala la skill de migración: ejecuta el codemod, registra argsKey para los schemas que dependen de los argumentos y se encarga de los casos que el codemod no puede resolver (campos de clase, asignación dinámica de métodos, bloques <script> de SFC, helpers personalizados):

npx skills add reactive/data-client --skill data-client-v0.18-migration
Ejecútalo en tu terminal

De lo contrario, ejecuta el codemod directamente; cubre los casos comunes:

npx jscodeshift -t https://dataclient.io/codemods/v0.18.js --extensions=ts,tsx,js,jsx src/
Ejecútalo en tu terminal

v0.16: Obtención en paralelo, Collection.move, Lazy

· 14 min de lectura
Nathaniel Tucker
Creador de Reactive Data Client

Nuevas funcionalidades:

Otras mejoras:

Collection.move facilita mover entidades entre Collections con una sola operación:

import { useController } from '@data-client/react';
import { TaskResource, type Task } from './TaskResource';

export default function TaskCard({ task }: { task: Task }) {
  const handleMove = () => ctrl.fetch(
    TaskResource.getList.move,
    { id: task.id },
    { id: task.id, status: task.status === 'backlog' ? 'in-progress' : 'backlog' },
  );
  const ctrl = useController();
  return (
    <div className="listItem">
      <span style={{ flex: 1 }}>{task.title}</span>
      <button onClick={handleMove}>
        {task.status === 'backlog' ? '\u25bc' : '\u25b2'}
      </button>
    </div>
  );
}
Vista previa en vivo

Cambios incompatibles:

Actualiza con un codemod automatizado para todos los cambios incompatibles:

npx jscodeshift -t https://dataclient.io/codemods/v0.16.js --extensions=ts,tsx,js,jsx src/
Ejecútalo en tu terminal

v0.15: Vue 3, Collection.remove, actualización de useDebounce

· 10 min de lectura
Nathaniel Tucker
Creador de Reactive Data Client

Nuevas plataformas:

  • Vue 3 con composables completos: useSuspense, useCache, useDLE, useController, useQuery

Nuevas funcionalidades:

Rendimiento:

Cambios incompatibles:

Actualiza con un codemod automatizado para los cambios incompatibles más comunes:

npx jscodeshift -t https://dataclient.io/codemods/v0.15.js --extensions=ts,tsx,js,jsx src/
Ejecútalo en tu terminal

v0.14: APIs de Manager simplificadas, rendimiento y GC

· 13 min de lectura
Nathaniel Tucker
Creador de Reactive Data Client

Managers:

Las APIs de Manager se han simplificado: formas de acción más planas reducen la complejidad del código y mejoran el rendimiento al minimizar las entradas distintas de la caché inline, mientras que APIs de configuración más simples hacen que la personalización sea más agradable.

Rendimiento:

Schema/Modelado de datos:

Pruebas:

Recolección de basura:

  • GCPolicy: recolección de basura automática de datos obsoletos

Cambios incompatibles:

v0.13: NextJS App Router, Expo, React 19, Redux

· 9 min de lectura
Nathaniel Tucker
Creador de Reactive Data Client

Plataformas:

Pruebas:

Paquetes movidos dentro de @data-client/react:

  • Redux
  • Hooks

Otras mejoras

Cambios incompatibles:

nota

La versión 0.12 se omitió debido a un percance en la publicación.

v0.11 Queries, Queryable y useQuery

· 12 min de lectura
Nathaniel Tucker
Creador de Reactive Data Client

Además de las ventajas de rendimiento e integridad de datos de normalizar el estado, obtenemos el beneficio adicional de poder acceder al store y consultarlo directamente de forma segura.

En esta versión ajustamos y simplificamos esta funcionalidad unificándola en torno al concepto de Schemas Queryable. Estos incluyen Entity, All, Collection, Query y Union

El mayor impacto de este cambio es la introducción de un nuevo hook, useQuery(), que permite búsquedas directas en el store usando los Schemas Queryable.

class User extends Entity {
username = '';
id = '';
groupId = '';
pk() {
return this.id;
}
static index = ['username' as const];
}

const bob = useQuery(User, { username: 'bob' });
const bob = useQuery(User, { id: '5' });

De manera similar, podemos buscar Queryables con el controller y el snapshot usando controller.get

const bob = snapshot.get(User, { username: 'bob' });

Adicionalmente, hemos invertido en más mejoras de rendimiento, que resultan en un aumento de rendimiento de alrededor de 2x para la mayoría de las operaciones y en que las Queries sean 16x más rápidas.

Guía de migración

Cambios incompatibles:

Otros puntos destacados:

v0.10: Manejo consistente de null, utilidades de URL

· 4 min de lectura
Nathaniel Tucker
Creador de Reactive Data Client

Esta versión se centra en la consistencia y la extensibilidad. El manejo de null ahora es uniforme en todos los tipos de schema, lo que hace que las transformaciones de datos sean más predecibles. Las nuevas utilidades de URL permiten construir URLs personalizadas y codificar parámetros de búsqueda.

class MyEndpoint<O extends RestGenerics = any> extends RestEndpoint<O> {
searchToString(searchParams) {
// Use qs library for complex nested object encoding
return qs.stringify(searchParams);
}
}
Guía de migración

Cambios incompatibles:

Otras mejoras:

v0.9: DevTools, Resource.extend y limpieza de APIs heredadas

· 8 min de lectura
Nathaniel Tucker
Creador de Reactive Data Client

Esta versión se centra en la experiencia de desarrollo con un botón de devtools que aparece en modo de desarrollo, un mejor historial de DevTools y nuevos métodos del controller como controller.expireAll() y controller.fetchIfStale().

Resource.extend() ofrece tres formas potentes de personalizar recursos: agregar nuevos endpoints, sobrescribir los existentes o derivar a partir de los endpoints base.

const UserResource = createResource({
path: '/users/:id',
schema: User,
}).extend('current', {
path: '/users/current',
});
Guía de migración

Cambios incompatibles:

v0.2 Controller.fetch, getHeaders asíncrono, Collections

· 6 min de lectura
Nathaniel Tucker
Creador de Reactive Data Client

Las Collections permiten extender fácilmente Arrays y Objects agregando nuevos miembros con push o unshift. El nombre proviene de las Collections de Backbone.

Las Collections son ahora el schema predeterminado de Resource.getList.

import { useController } from '@data-client/react';
import { TodoResource } from './TodoResource';

export default function CreateTodo({ userId }: { userId: number }) {
  const ctrl = useController();
  const handleKeyDown = async e => {
    if (e.key === 'Enter') {
      ctrl.fetch(TodoResource.getList.push, {
        userId,
        title: e.currentTarget.value,
        id: Math.random(),
      });
      e.currentTarget.value = '';
    }
  };
  return (
    <div className="listItem nogap">
      <label>
        <input type="checkbox" name="new" checked={false} disabled />
        <input type="text" onKeyDown={handleKeyDown} />
      </label>
    </div>
  );
}
Vista previa en vivo

Actualizar es bastante sencillo, ya que se introdujeron @data-client/rest/next y @data-client/react/next para permitir la adopción incremental de las nuevas APIs que cambian en esta versión. Esto convierte la actualización en sí en un simple cambio de nombre en los imports.

Otros aspectos destacados incluyen

Para todos los detalles, sigue leyendo: