bigbuffet-rw/app/soapbox/entity-store/hooks/useEntity.ts

65 lines
1.6 KiB
TypeScript
Raw Normal View History

import { useEffect, useState } from 'react';
import z from 'zod';
2022-12-04 14:58:13 -08:00
import { useApi, useAppDispatch, useAppSelector } from 'soapbox/hooks';
import { importEntities } from '../actions';
import type { Entity } from '../types';
type EntityPath = [entityType: string, entityId: string]
/** Additional options for the hook. */
interface UseEntityOpts<TEntity> {
/** A zod schema to parse the API entity. */
schema?: z.ZodType<TEntity, z.ZodTypeDef, any>
/** Whether to refetch this entity every time the hook mounts, even if it's already in the store. */
refetch?: boolean
}
function useEntity<TEntity extends Entity>(
path: EntityPath,
endpoint: string,
opts: UseEntityOpts<TEntity> = {},
) {
2022-12-04 14:58:13 -08:00
const api = useApi();
const dispatch = useAppDispatch();
const [entityType, entityId] = path;
const defaultSchema = z.custom<TEntity>();
const schema = opts.schema || defaultSchema;
const entity = useAppSelector(state => state.entities[entityType]?.store[entityId] as TEntity | undefined);
2022-12-04 14:58:13 -08:00
const [isFetching, setIsFetching] = useState(false);
const isLoading = isFetching && !entity;
const fetchEntity = () => {
setIsFetching(true);
api.get(endpoint).then(({ data }) => {
const entity = schema.parse(data);
dispatch(importEntities([entity], entityType));
2022-12-04 14:58:13 -08:00
setIsFetching(false);
}).catch(() => {
setIsFetching(false);
});
};
useEffect(() => {
if (!entity || opts.refetch) {
fetchEntity();
}
}, []);
2022-12-04 14:58:13 -08:00
return {
entity,
fetchEntity,
isFetching,
isLoading,
};
}
export {
useEntity,
};