EntityStore: allow passing a parser function to parse the entities
This commit is contained in:
parent
9964491da5
commit
250b009635
2 changed files with 42 additions and 5 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue