Pensando em Schemas
Considere um post de blog típico. A resposta da API para um único post pode ser parecida com esta:
{
"id": "123",
"author": {
"id": "1",
"name": "Paul"
},
"title": "My awesome blog post",
"comments": [
{
"id": "324",
"createdAt": "2013-05-29T00:00:00-04:00",
"commenter": {
"id": "2",
"name": "Nicole"
}
},
{
"id": "544",
"createdAt": "2013-05-30T00:00:00-04:00",
"commenter": {
"id": "1",
"name": "Paul"
}
}
]
}
Definições declarativas
Temos dois tipos de entity aninhados dentro de article: users e comments. Usando vários schemas, podemos normalizar os três tipos de entity:
- TypeScript
- JavaScript
import { schema, Entity } from '@data-client/endpoint';
import { Temporal } from 'temporal-polyfill';
class User extends Entity {
id = '';
name = '';
}
class Comment extends Entity {
id = '';
createdAt = Temporal.Instant.fromEpochMilliseconds(0);
commenter = User.fromJS();
static schema = {
commenter: User,
createdAt: Temporal.Instant.from,
};
}
class Article extends Entity {
id = '';
title = '';
author = User.fromJS();
comments: Comment[] = [];
static schema = {
author: User,
comments: [Comment],
};
}
import { schema, Entity } from '@data-client/endpoint';
import { Temporal } from 'temporal-polyfill';
class User extends Entity { }
class Comment extends Entity {
static schema = {
commenter: User,
createdAt: Temporal.Instant.from,
};
}
class Article extends Entity {
static schema = {
author: User,
comments: [Comment],
};
}
Normalize
import { normalize } from '@data-client/normalizr';
const args = [{ id: '123' }];
const normalizedData = normalize(Article, originalData, args);
Agora, normalizedData criará uma única fonte de verdade serializável para todas as entities:
{
result: "123",
entities: {
articles: {
"123": {
id: "123",
author: "1",
title: "My awesome blog post",
comments: [ "324", "544" ]
}
},
users: {
"1": { "id": "1", "name": "Paul" },
"2": { "id": "2", "name": "Nicole" }
},
comments: {
"324": {
id: "324",
createdAt: "2013-05-29T00:00:00-04:00",
commenter: "2"
},
"544": {
id: "544",
createdAt: "2013-05-30T00:00:00-04:00",
commenter: "1"
}
}
},
// contents excluded for brevity
indexes,
entitiesMeta,
}
Denormalize
import { denormalize } from '@data-client/normalizr';
const denormalizedData = denormalize(
Article,
normalizedData.result,
normalizedData.entities,
args,
);
Agora, denormalizedData instanciará as classes, garantindo que todas as instâncias do mesmo membro (como Paul) sejam referencialmente iguais:
Article {
id: '123',
title: 'My awesome blog post',
author: User { id: '1', name: 'Paul' },
comments: [
Comment {
id: '324',
createdAt: Instant [Temporal.Instant] {},
commenter: [User { id: '2', name: 'Nicole' }]
},
Comment {
id: '544',
createdAt: Instant [Temporal.Instant] {},
commenter: [User { id: '1', name: 'Paul' }]
}
]
}
MemoCache
MemoCache é um singleton que pode ser usado para manter a igualdade referencial entre chamadas e
também para um ganho de desempenho potencial de 2000%. Seus métodos são memoizados.
memo.denormalize
import { MemoCache } from '@data-client/normalizr';
// you can construct a new memo anytime you want to reset the cache
const memo = new MemoCache();
const { data, paths } = memo.denormalize(
Article,
normalizedData.result,
normalizedData.entities,
args,
);
const { data: data2 } = memo.denormalize(
Article,
normalizedData.result,
normalizedData.entities,
args,
);
// referential equality maintained between calls
assert(data === data2);
memo.denormalize() é como o denormalize() acima, mas inclui paths como parte do valor de retorno. paths
é um Array com os caminhos de todas as entities incluídas no resultado.
memo.query
memo.query() permite desnormalizar um Queryable apenas com base nos args, em vez de uma entrada normalizada.
const data = memo.query(
Article,
args,
normalizedData,
);
Queryable
Schemas Queryable permitem acessar o store sem um endpoint. Eles fazem isso usando o
método queryKey, que produz os resultados normalmente armazenados no cache do endpoint.
Isso permite seu uso nestes casos adicionais:
- useQuery() - Renderização em React
- schema.Query() - Como entrada para produzir uma memoização computada.
- Via ctrl.get/snap.get
- memo.query()
- Melhorar o desempenho de useSuspense e useDLE renderizando antes da resolução do endpoint
Querables incluem Entity, All, Collection, Query,
Union e Scalar. Campos Lazy produzem um Queryable por meio do accessor .query.
interface Queryable {
queryKey(
args: readonly any[],
queryKey: (...args: any) => any,
getEntity: GetEntity,
getIndex: GetIndex,
// `{}` means non-void
): {};
}
Visão geral dos schemas
| Tipo de dado | Mutável | Schema | Descrição | Queryable |
|---|---|---|---|---|
| Objeto | ✅ | Entity | um único objeto único | ✅ |
| ✅ | Union(Entity) | objetos polimórficos (A | B) | ✅ | |
| 🛑 | Object | chaves conhecidas estaticamente | 🛑 | |
| Invalidate(Entity) | excluir uma entity | 🛑 | ||
| Lista | ✅ | Collection(Array) | listas expansíveis | ✅ |
| 🛑 | Array | listas imutáveis | 🛑 | |
| All | lista de todas as entities de um tipo | ✅ | ||
| Mapa | ✅ | Collection(Values) | mapas expansíveis | ✅ |
| 🛑 | Values | mapas imutáveis | 🛑 | |
| Scalar | ✅ | Scalar | campos de entity dependentes de lens | ✅ |
| qualquer | Query(Queryable) | transformações personalizadas memoizadas | ✅ | |
| Lazy(Schema) | desnormalização adiada | ✅ |