import { useEffect } from 'react'; import z from 'zod'; import { useAppDispatch, useAppSelector, useLoading } from 'soapbox/hooks'; import { importEntities } from '../actions'; import type { Entity } from '../types'; import type { EntitySchema, EntityPath, EntityFn } from './types'; /** Additional options for the hook. */ interface UseEntityOpts { /** A zod schema to parse the API entity. */ schema?: EntitySchema /** Whether to refetch this entity every time the hook mounts, even if it's already in the store. */ refetch?: boolean } function useEntity( path: EntityPath, entityFn: EntityFn, opts: UseEntityOpts = {}, ) { const [isFetching, setPromise] = useLoading(true); const dispatch = useAppDispatch(); const [entityType, entityId] = path; const defaultSchema = z.custom(); const schema = opts.schema || defaultSchema; const entity = useAppSelector(state => state.entities[entityType]?.store[entityId] as TEntity | undefined); const isLoading = isFetching && !entity; const fetchEntity = async () => { try { const response = await setPromise(entityFn()); const entity = schema.parse(response.data); dispatch(importEntities([entity], entityType)); } catch (e) { // do nothing } }; useEffect(() => { if (!entity || opts.refetch) { fetchEntity(); } }, []); return { entity, fetchEntity, isFetching, isLoading, }; } export { useEntity, };