useDataSource()
import { Dispatch, SetStateAction, useEffect, useState } from "react"; export interface IData<T> { data: T; loading: boolean; loaded: boolean; setData: Dispatch<SetStateAction<T>>; updateData: () => any; } export function useData<T>(fetchFn: () => Promise<T>, deps: any = []): IData<T> { const [trigger, setTrigger] = useState<boolean>(false); const [data, setData] = useState<T>(); const [loading, setLoading] = useState(true); const [loaded, setLoaded] = useState(false); useEffect(() => { async function fetch() { setLoading(true); setLoaded(false); try { const newData = await fetchFn(); setData(newData); setLoaded(true); } catch (e) { console.error(e); setData(undefined); } finally { setLoading(false); } } fetch(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [...deps, trigger]); return { data, loading, loaded, setData, updateData: () => setTrigger(!trigger) } as IData<T>; };
useInterval()
/* eslint-disable react-hooks/exhaustive-deps */ import { useEffect, useState } from "react"; function useInterval(delay: number) { const [data, setData] = useState<number>(0); let index = 0; useEffect(() => { if (!delay && delay !== 0) { return; } const id = setInterval(() => setData(index++), delay); return () => clearInterval(id); }, [delay]); return [data, setData]; } export default useInterval;
useObservable() + useFetch() example
/* eslint-disable react-hooks/exhaustive-deps */ import { useEffect, useState } from "react"; import { Observable } from "rxjs"; import { Select } from "antd"; import { IUser } from "../../domain/user/IUser"; import { appDomain, translate } from "../../domain/wiring/AppDomain"; export function useFetch<T>(fetchFn: () => Promise<T[]>, deps: any[] = []): [T[], Dispatch<SetStateAction<T[]>>] { const [data, setData] = useState<T[]>([]); useEffect(() => { async function fetch() { const newData = await fetchFn(); setData(newData); } fetch(); }, deps); return [data, setData]; }; interface IProps { value?: string; onChange: (value: string) => void; } export function UserSelector(props: IProps) { const company = useObservable(appDomain.ICompanyService.companyObservable()); const [users] = useFetch<IUser>(() => appDomain.IRequestUser.getAllUsersForCompany(company?.id), [company?.id]); return <Select showSearch style={{ minWidth: 200 }} placeholder={translate("general.select.user")} optionFilterProp="children" value={props.value} onChange={props.onChange}> {users.map((x: IUser, i: number) => (<Select.Option key={x.id} value={x.id!}>{x.fullName}</Select.Option>))} </Select>; } export function useObservable<T>(subject: Observable<T | undefined>, initialValue?: T): T | undefined { const [data, setData] = useState<T | undefined>(initialValue); useEffect(() => { if (!subject) { return; } const subscription = subject.subscribe((x: any) => { setData(x); }); return () => subscription.unsubscribe(); }, [subject]); return data; } export function useObservableReducer<T>(subject?: Observable<T | undefined>): T | undefined { const [data, forceUpdate] = useReducer(x => x + 1, 0); useEffect(() => { if (!subject) { return; } const subscription = subject.subscribe(() => { forceUpdate(); }); return () => subscription.unsubscribe(); }, [subject]); return data; }
Old code (with useSubscription)
/* eslint-disable react-hooks/exhaustive-deps */ import { useEffect, useState, Dispatch, SetStateAction } from "react"; import { Subscription } from "rxjs"; import { Select } from "antd"; import { IUser } from "../../domain/user/IUser"; import { appDomain, translate } from "../../domain/wiring/AppDomain"; type ChangeFn<T> = (next: T | undefined) => any; type SubscribeFn<T> = (nextFn: ChangeFn<T>) => Subscription; export function useSubscription<T>(subscribeFn: SubscribeFn<T>, deps?: any[]): T | undefined { const [data, setData] = useState<T | undefined>(undefined); useEffect(() => { function onChange(next: T | undefined) { setData(next); } const subscription = subscribeFn(onChange); return () => subscription.unsubscribe(); }, deps); return data; }; export function useDatasource<T>(fetchFn: () => Promise<T[]>, deps: any[]): [T[], Dispatch<SetStateAction<T[]>>] { const [data, setData] = useState<T[]>([]); useEffect(() => { async function fetch() { const newData = await fetchFn(); setData(newData); } fetch(); }, deps); return [data, setData]; }; /* Example usage */ interface IProps { value?: string; onChange: (value: string) => void; } export function UserSelector(props: IProps) { const company = useSubscription(appDomain.ICompanyService.subscribeToCompany.bind(appDomain.ICompanyService)); const [users] = useDatasource<IUser>(() => appDomain.IRequestUser.getAllUsersForCompany(company?.id), [company?.id]); return <Select showSearch style={{ minWidth: 200 }} placeholder={translate("general.select.user")} optionFilterProp="children" value={props.value} onChange={props.onChange}> {users.map((x: IUser, i: number) => (<Select.Option key={x.id} value={x.id!}>{x.fullName}</Select.Option>))} </Select>; }
687600cookie-checkReact Hooks: Helpers