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.
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.
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.
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.
{"post":{"id":"5","author":{"id":"123","name":"Jim"},"content":"Happy day","createdAt":"2019-01-23T06:07:48.311Z"}}
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'; }
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.
No agregues tu clave primaria como id a la lista de índices, ya que ya está optimizada.