Saltar al contenido principal

GQLEntity

GraphQL tiene una forma estándar de definir el pk, que es con un campo id.

GQLEntity incluye automáticamente un campo id, que se usa para el pk.

extends

GQLEntity extiende Entity

Uso​

import { GQLEntity } from '@data-client/graphql';
import { User } from './User';

export class Article extends GQLEntity {
  title = '';
  content = '';
  author = User.fromJS();
  tags: string[] = [];
  createdAt = Temporal.Instant.fromEpochMilliseconds(0);

  static schema = {
    author: User,
    createdAt: Temporal.Instant.from,
  };
}

static schema es una definición declarativa de los campos que se deben procesar. En este caso, author es otra Entity que se debe extraer, y createdAt se convertirá de un string a un objeto Date.

consejo

Las entidades se vinculan a GQLEndpoints mediante el segundo argumento de query o mutate.

Otras sobrescrituras de miembros estáticos permiten personalizar el ciclo de vida de los datos, como se ve a continuación.

Ciclo de vida de los datos​

Métodos​

pk(parent?, key?, args?): string?​

PK significa primary key (clave primaria) y está pensado para proporcionar un medio estándar de obtener un identificador de clave para cualquier Entity.

GraphQL usa el campo id como el identificador global de objeto estándar.

pk() {
return this.id;
}

static key: string​

Esto define la clave de la Entity en sí, en lugar de la de una instancia. Debe ser un valor único a nivel global.

aviso

Por defecto es this.name; sin embargo, esto puede fallar en compilaciones de producción que cambian los nombres de las clases. Esto suele conocerse como class name mangling.

En esos casos puedes sobrescribir key o deshabilitar el mangling de clases.

class User extends GQLEntity {
username = '';

static key = 'User';
}

static process(input, parent, key, args): processedEntity​

Se ejecuta al inicio de la normalización de esta entidad. El valor de retorno se guarda en el store.

Por defecto simplemente copia la respuesta ({...input})

Cómo sobrescribirlo para construir búsquedas inversas para datos relacionales

static mergeWithStore(existingMeta, incomingMeta, existing, incoming): mergedValue​

static mergeWithStore(
existingMeta: {
date: number;
fetchedAt: number;
},
incomingMeta: { date: number; fetchedAt: number },
existing: any,
incoming: any,
) {
const shouldUpdate = this.shouldUpdate(
existingMeta,
incomingMeta,
existing,
incoming,
);

if (shouldUpdate) {
// distinct types are not mergeable (like delete symbol), so just replace
if (typeof incoming !== typeof existing) {
return incoming;
} else {
return this.shouldReorder(
existingMeta,
incomingMeta,
existing,
incoming,
)
? this.merge(incoming, existing)
: this.merge(existing, incoming);
}
} else {
return existing;
}
}

mergeWithStore() se llama durante la normalización cuando una entidad procesada ya existe en el store.

Esto llama a shouldUpdate(), shouldReorder() y, potencialmente, a merge()

static shouldUpdate(existingMeta, incomingMeta, existing, incoming): boolean​

static shouldUpdate(
existingMeta: { date: number; fetchedAt: number },
incomingMeta: { date: number; fetchedAt: number },
existing: any,
incoming: any,
) {
return existingMeta.fetchedAt <= incomingMeta.fetchedAt;
}

Evitar actualizaciones​

shouldUpdate también se puede usar para interrumpir la actualización de una entidad.

import deepEqual from 'deep-equal';

class Article extends GQLEntity {
title = '';
content = '';
published = false;

static shouldUpdate(
existingMeta: { date: number; fetchedAt: number },
incomingMeta: { date: number; fetchedAt: number },
existing: any,
incoming: any,
) {
return !deepEqual(incoming, existing);
}
}

static shouldReorder(existingMeta, incomingMeta, existing, incoming): boolean​

static shouldReorder(
existingMeta: { date: number; fetchedAt: number },
incomingMeta: { date: number; fetchedAt: number },
existing: any,
incoming: any,
) {
return incomingMeta.fetchedAt < existingMeta.fetchedAt;
}

Un valor de retorno true invertirá el orden de los argumentos de la entidad entrante y la que está en el store en el merge. Con el merge por defecto, esto hará que los campos de las entidades existentes sobrescriban a los de las entrantes, en lugar de al revés.

Ejemplo​

import { GQLEntity } from '@data-client/graphql';

export class LatestPriceEntity extends GQLEntity {
  updatedAt = 0;
  price = '0.0';
  symbol = '';

  static shouldReorder(
    existingMeta: { date: number; fetchedAt: number },
    incomingMeta: { date: number; fetchedAt: number },
    existing: { updatedAt: number },
    incoming: { updatedAt: number },
  ) {
    return incoming.updatedAt < existing.updatedAt;
  }
}

static merge(existing, incoming): mergedValue​

static merge(existing: any, incoming: any) {
return {
...existing,
...incoming,
};
}

Merge se usa para manejar los casos en que ya existe una entidad entrante. Se llama directamente cuando se encuentra la misma entidad en una sola respuesta. Por defecto también se llama cuando mergeWithStore() determina que la entidad entrante debe fusionarse con una entidad ya persistida en el store de Reactive Data Client.

Cómo sobrescribirlo para construir búsquedas inversas para datos relacionales

static mergeMetaWithStore(existingMeta, incomingMeta, existing, incoming): meta​

static mergeMetaWithStore(
existingMeta: {
expiresAt: number;
date: number;
fetchedAt: number;
},
incomingMeta: { expiresAt: number; date: number; fetchedAt: number },
existing: any,
incoming: any,
) {
return this.shouldReorder(existingMeta, incomingMeta, existing, incoming)
? existingMeta
: incomingMeta;
}

mergeMetaWithStore() se llama durante la normalización cuando una entidad procesada ya existe en el store.

static queryKey(args, queryKey, getEntity, getIndex): pk?​

Este método permite que las Entities sean Queryable, es decir, que se pueda acceder al store sin un endpoint.

Sobrescribirlo permite personalizar o deshabilitar por completo este comportamiento.

Devolver undefined deshabilitará este comportamiento.

Devolver un string pk intentará buscar esta entidad y usarla en la respuesta.

Cuando se usa, la política de caducidad se calcula a partir de los metadatos propios de la entidad.

Por defecto usa el primer argumento para buscar en pk() y indexes

static createIfValid(processedEntity): Entity | undefined​

Se llama al desnormalizar una entidad. Crea una instancia de esta clase si se considera 'válida'.

Un retorno undefined resultará en un estado de caducidad Invalid, como Invalidate.

La caducidad Invalid generalmente significa que los hooks entrarán en estado de carga e intentarán un nuevo fetch.

static createIfValid(props): AbstractInstanceType<this> | undefined {
if (this.validate(props)) {
return undefined as any;
}
return this.fromJS(props);
}

static validate(processedEntity): errorMessage?​

Se ejecuta tanto en la normalización como en la desnormalización. Devolver un string indica un error (el string es el mensaje).

Durante la normalización, un fallo de validación producirá un error para ese fetch.

Durante la desnormalización, un fallo de validación marcará ese resultado como 'invalid' y, por tanto, bloqueará mientras se obtiene un resultado.

Por defecto hace algunas comprobaciones básicas de existencia de campos, solo en modo de desarrollo. Sobrescríbelo para deshabilitarlo o personalizarlo.

Usar la validación en endpoints con campos incompletos

static fromJS(props): Entity​

Método de fábrica que copia las props a una nueva instancia. Úsalo en lugar de new MyEntity(), para asegurar que se sobrescriban las props por defecto.

Campos​

static schema: { [k: keyof this]: Schema }​

Define los miembros de entidades relacionadas, o la deserialización de campos como Date y BigNumber.

Fixtures
query getPost($id: ID!) { post(id: $id) { id author createdAt content title } } {"id":"123"}
{"post":{"id":"5","author":{"id":"123","name":"Jim"},"content":"Happy day","createdAt":"2019-01-23T06:07:48.311Z"}}
▶User
▶Post
import { GQLEntity } from '@data-client/graphql';
import { Temporal } from 'temporal-polyfill';
import { User } from './User';

export class Post extends GQLEntity {
  author = User.fromJS({});
  createdAt = Temporal.Instant.fromEpochMilliseconds(0);
  content = '';
  title = '';

  static schema = {
    author: User,
    createdAt: Temporal.Instant.from,
  };
  static key = 'Post';
}
▶PostPage
Resultado
Store▶

Miembros opcionales​

Las referencias a entidades aquí cuyos valores por defecto en la propia definición del Record se consideran 'opcionales'

class User extends GQLEntity {
friend: User | null = null; // this field is optional
lastUpdated = Temporal.Instant.fromEpochMilliseconds(0);

static schema = {
friend: User,
lastUpdated: Temporal.Instant.from,
};
}

static indexes?: (keyof this)[]​

Los índices aumentan el rendimiento al hacer búsquedas basadas en esos parámetros. Agrega a la lista los nombres de campo (como slug, username) que quieras enviar como params para buscar más adelante.

nota

No agregues tu clave primaria como id a la lista de índices, ya que ya está optimizada.