Computed Properties
Singular computations
Entity classes are just normal classes, so any common derived data can just be added as getters to the class itself.
import { All, Entity, Query } from '@data-client/rest';
class User extends Entity {
id = '';
firstName = '';
lastName = '';
username = '';
email = '';
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
static key = 'User';
}
If the computations are expensive feel free to add some memoization.
import { All, Entity, Query } from '@data-client/rest';
import memoize from 'nano-memoize';
class User extends Entity {
truelyExpensiveValue = memoize(() => {
// compute that expensive thing!
});
}
If you simply want to deserialize a field to a more useful form like Temporal.Instant or BigNumber, you can use the declarative static schema.
import { All, Entity, Query } from '@data-client/rest';
import BigNumber from 'bignumber.js';
class User extends Entity {
id = '';
firstName = '';
lastName = '';
createdAt = Temporal.Instant.fromEpochMilliseconds(0);
lifetimeBlinkCount = BigNumber(0);
static key = 'User';
static schema = {
createdAt: Temporal.Instant.from,
lifetimeBlinkCount: BigNumber,
};
}
Global computations
Query derives data from many entities, like a cart's totals. This is known as an aggregate. Define it once beside your resources, then read it anywhere with useQuery().
import { Query } from '@data-client/rest'; import { CartResource } from './resources/Cart'; export const freeShippingAt = 50; export const getCartTotals = new Query(CartResource.getList.schema, items => { const count = items.reduce((sum, item) => sum + item.quantity, 0); const subtotal = items.reduce((sum, item) => sum + item.total, 0); const toFreeShipping = Math.max(0, freeShippingAt - subtotal); const shipping = subtotal && toFreeShipping ? 5 : 0; return { count, subtotal, shipping, total: subtotal + shipping, toFreeShipping, }; });
Here the cart badge, free-shipping bar, order summary and checkout button all read getCartTotals.
It runs once per change, however many components read it, and returns the same object until a
CartItem or Product it read changes. So every view agrees, even mid
optimistic update.
Do not compute aggregates in components, where each copy can drift. Instead, put them in a Query.