export interface IUseDataState<T> { data: T | undefined; trigger: number; revision: number; loading: boolean; loaded: boolean; initialLoading: boolean; } export function useData<T>(fetchFn: () => Promise<T>, deps: any = []): IData<T> { const [state, setState] = useObjectState<IUseDataState<T>>({ revision: 0, trigger: 0, loaded: false, loading: true, initialLoading: true, data: undefined }); useEffectAsync(async () => { if (!state.initialLoading) { setState({ loading: true, loaded: false }); } let newState: Partial<IUseDataState<T>> = {}; try { const newData = await fetchFn(); newState = { data: newData, loaded: true, revision: state.revision + 1 }; } catch (e) { console.error(e); newState = { data: undefined, revision: state.revision + 1 }; } finally { setState({ ...newState, loading: false, initialLoading: false }); } }, [...deps, state.trigger]); return { data: state.data, revision: state.revision, loading: state.loading, loaded: state.loaded, initialLoading: state.initialLoading, setData: (data: T) => setState({ data: data, revision: state.revision + 1 }), updateData: () => setState({ trigger: state.trigger + 1 }) } as IData<T>; }
Example component:
export function UserInfo(props: { userId: string }): React.ReactElement { const ds = useData(() => appDomain.IUserService.loadUser(props.userId), [props.userId]); const user = ds.data; return <div> Loading: {ds.loading} User name: {user?.name} <button type="button" onClick={ds.updateData}>Refresh</button> </div>; }
782700cookie-checkReact Hook: useData()