EntityStore: allow passing a parser function to parse the entities

This commit is contained in:
Alex Gleason 2023-03-09 12:32:50 -06:00
parent 9964491da5
commit 250b009635
No known key found for this signature in database
GPG key ID: 7211D1F99744FBB7
2 changed files with 42 additions and 5 deletions

View file

@ -5,14 +5,37 @@ import { entitiesFetchFail, entitiesFetchRequest, entitiesFetchSuccess } from '.
import type { Entity } from '../types';
type EntityPath = [entityType: string, listKey: string]
/** Tells us where to find/store the entity in the cache. */
type EntityPath = [
/** Name of the entity type for use in the global cache, eg `'Notification'`. */
entityType: string,
/** Name of a particular index of this entity type. You can use empty-string (`''`) if you don't need separate lists. */
listKey: string,
]
function useEntities<TEntity extends Entity>(path: EntityPath, endpoint: string) {
/** Additional options for the hook. */
interface UseEntitiesOpts<TEntity> {
/** A parser function that returns the desired type, or undefined if validation fails. */
parser?: (entity: unknown) => TEntity | undefined
}
/** A hook for fetching and displaying API entities. */
function useEntities<TEntity extends Entity>(
/** Tells us where to find/store the entity in the cache. */
path: EntityPath,
/** API route to GET, eg `'/api/v1/notifications'` */
endpoint: string,
/** Additional options for the hook. */
opts: UseEntitiesOpts<TEntity> = {},
) {
const api = useApi();
const dispatch = useAppDispatch();
const [entityType, listKey] = path;
const defaultParser = (entity: unknown) => entity as TEntity;
const parseEntity = opts.parser || defaultParser;
const cache = useAppSelector(state => state.entities[entityType]);
const list = cache?.lists[listKey];
@ -20,7 +43,7 @@ function useEntities<TEntity extends Entity>(path: EntityPath, endpoint: string)
const entities: readonly TEntity[] = entityIds ? (
Array.from(entityIds).reduce<TEntity[]>((result, id) => {
const entity = cache?.store[id] as TEntity | undefined;
const entity = parseEntity(cache?.store[id] as unknown);
if (entity) {
result.push(entity);
}

View file

@ -8,12 +8,26 @@ import type { Entity } from '../types';
type EntityPath = [entityType: string, entityId: string]
function useEntity<TEntity extends Entity>(path: EntityPath, endpoint: string) {
/** Additional options for the hook. */
interface UseEntityOpts<TEntity> {
/** A parser function that returns the desired type, or undefined if validation fails. */
parser?: (entity: unknown) => TEntity | undefined
}
function useEntity<TEntity extends Entity>(
path: EntityPath,
endpoint: string,
opts: UseEntityOpts<TEntity> = {},
) {
const api = useApi();
const dispatch = useAppDispatch();
const [entityType, entityId] = path;
const entity = useAppSelector(state => state.entities[entityType]?.store[entityId]) as TEntity | undefined;
const defaultParser = (entity: unknown) => entity as TEntity;
const parseEntity = opts.parser || defaultParser;
const entity = useAppSelector(state => parseEntity(state.entities[entityType]?.store[entityId]));
const [isFetching, setIsFetching] = useState(false);
const isLoading = isFetching && !entity;