mirror of
https://github.com/sergeyyarkov/educt-client.git
synced 2026-10-11 10:19:22 +03:00
feat: added useAsync hook
This commit is contained in:
parent
80ddcead4f
commit
bf55565eed
41
src/hooks/useAsync.ts
Normal file
41
src/hooks/useAsync.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import useIsMountedRef from './useIsMountedRef';
|
||||
|
||||
type StateType<DataResultT> = {
|
||||
status: 'idle' | 'pending' | 'success' | 'error';
|
||||
error: any;
|
||||
data: DataResultT | null;
|
||||
isLoading: boolean;
|
||||
};
|
||||
|
||||
const useAsync = <T, P extends unknown[]>(callback: (...params: P) => Promise<T>) => {
|
||||
const [state, setState] = useState<StateType<T>>({ status: 'idle', error: null, data: null, isLoading: false });
|
||||
const isMountedRef = useIsMountedRef();
|
||||
|
||||
const execute = useCallback(
|
||||
async (...params: P) => {
|
||||
try {
|
||||
setState(s => ({ ...s, isLoading: true, status: 'pending', data: null, error: null }));
|
||||
const res = await callback(...params);
|
||||
if (isMountedRef.current) {
|
||||
setState(s => ({ ...s, status: 'success', data: res }));
|
||||
}
|
||||
return res;
|
||||
} catch (error: any) {
|
||||
if (isMountedRef.current) {
|
||||
setState(s => ({ ...s, status: 'error', error }));
|
||||
}
|
||||
throw error;
|
||||
} finally {
|
||||
if (isMountedRef.current) {
|
||||
setState(s => ({ ...s, isLoading: false }));
|
||||
}
|
||||
}
|
||||
},
|
||||
[callback]
|
||||
);
|
||||
|
||||
return { execute, ...state };
|
||||
};
|
||||
|
||||
export default useAsync;
|
||||
@ -1,27 +1,16 @@
|
||||
import { useToast } from '@chakra-ui/react';
|
||||
import { IApiRespose, IToken } from '@educt/interfaces';
|
||||
import { useState } from 'react';
|
||||
import { useErrorHandler } from 'react-error-boundary';
|
||||
import useIsMountedRef from './useIsMountedRef';
|
||||
import useAsync from './useAsync';
|
||||
import { useRootStore } from './useRootStore';
|
||||
|
||||
type LoginStateType = {
|
||||
result: IApiRespose<IToken> | null;
|
||||
error: any;
|
||||
loading: boolean;
|
||||
fetched: boolean;
|
||||
};
|
||||
|
||||
const useLoginQuery = () => {
|
||||
const isMountedRef = useIsMountedRef();
|
||||
const { authStore } = useRootStore();
|
||||
const [state, setState] = useState<LoginStateType>({ result: null, error: null, loading: false, fetched: false });
|
||||
const toast = useToast();
|
||||
const handleError = useErrorHandler();
|
||||
|
||||
const login = async (login: string, password: string) => {
|
||||
try {
|
||||
setState(s => ({ ...s, loading: true }));
|
||||
const result = await authStore.login(login, password);
|
||||
toast({
|
||||
title: `👋 Welcome back!`,
|
||||
@ -29,14 +18,8 @@ const useLoginQuery = () => {
|
||||
isClosable: true,
|
||||
status: 'success',
|
||||
});
|
||||
|
||||
if (isMountedRef.current) {
|
||||
setState(s => ({ ...s, result }));
|
||||
}
|
||||
return result;
|
||||
} catch (error: any) {
|
||||
if (isMountedRef.current) {
|
||||
setState(s => ({ ...s, error }));
|
||||
}
|
||||
if (error.response) {
|
||||
/**
|
||||
* Handle response error
|
||||
@ -44,26 +27,23 @@ const useLoginQuery = () => {
|
||||
switch (error.response.status) {
|
||||
case 404:
|
||||
toast({ title: 'User not found in a system.', status: 'error', duration: 2000 });
|
||||
break;
|
||||
throw error;
|
||||
case 401:
|
||||
toast({ title: 'Invalid password.', status: 'error', duration: 2000 });
|
||||
break;
|
||||
throw error;
|
||||
default:
|
||||
handleError(error);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
console.error(error);
|
||||
handleError(error);
|
||||
}
|
||||
} finally {
|
||||
if (isMountedRef.current) {
|
||||
setState(s => ({ ...s, loading: false, fetched: true }));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return { ...state, login };
|
||||
const { execute, ...state } = useAsync<IApiRespose<IToken> | undefined, Parameters<typeof login>>(login);
|
||||
|
||||
return { login: execute, ...state };
|
||||
};
|
||||
|
||||
export default useLoginQuery;
|
||||
|
||||
@ -27,6 +27,7 @@ export interface ICourse {
|
||||
category: ICategory;
|
||||
lessons: ILesson[];
|
||||
students: Array<Pick<IUser, 'id' | 'first_name' | 'fullname' | 'last_name' | 'email'>>;
|
||||
color: ColorType | null;
|
||||
students_count: string;
|
||||
likes_count: string;
|
||||
lessons_count: string;
|
||||
|
||||
@ -44,14 +44,12 @@ const AuthForm: React.FC = () => {
|
||||
} = useForm<AuthInputType>({
|
||||
resolver: yupResolver(LoginSchema),
|
||||
});
|
||||
const { login, loading } = useLoginQuery();
|
||||
const { login, isLoading } = useLoginQuery();
|
||||
|
||||
/**
|
||||
* Login handler
|
||||
*/
|
||||
const onSubmit: SubmitHandler<AuthInputType> = data => {
|
||||
login(data.login, data.password).finally(() => reset());
|
||||
};
|
||||
const onSubmit: SubmitHandler<AuthInputType> = data => login(data.login, data.password).finally(() => reset());
|
||||
|
||||
return (
|
||||
<Box flexBasis='500px' borderWidth='1px' borderRadius='lg' p='8'>
|
||||
@ -87,7 +85,7 @@ const AuthForm: React.FC = () => {
|
||||
|
||||
<Button
|
||||
loadingText='Logging in...'
|
||||
isLoading={loading}
|
||||
isLoading={isLoading}
|
||||
type='submit'
|
||||
colorScheme='blue'
|
||||
variant='outline'
|
||||
|
||||
@ -23,7 +23,7 @@ import CourseStatusBadge from '@educt/components/CourseStatusBadge';
|
||||
* Hooks
|
||||
*/
|
||||
import { useHistory } from 'react-router';
|
||||
import { useColorMode } from '@chakra-ui/color-mode';
|
||||
import { useColorMode, useColorModeValue } from '@chakra-ui/color-mode';
|
||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||
|
||||
export type CourseItemPropsType = {
|
||||
@ -108,7 +108,7 @@ const CourseItem: React.FC<CourseItemPropsType> = ({ course, onDelete, onSetStat
|
||||
<Box
|
||||
position='relative'
|
||||
minHeight='200px'
|
||||
backgroundColor={colorMode === 'dark' ? 'gray.700' : 'gray.200'}
|
||||
backgroundColor={course.color?.hex || useColorModeValue('gray.200', 'gray.700')}
|
||||
borderTopLeftRadius='lg'
|
||||
borderTopRightRadius='lg'
|
||||
p='10px'
|
||||
|
||||
@ -69,7 +69,7 @@ const LessonItem: React.FC<LessonItemPropsType> = ({ lesson }) => {
|
||||
<Icon as={MdTimer} />
|
||||
</Text>
|
||||
<Text as='small' verticalAlign='middle'>
|
||||
~{moment(lesson.duration, 'HH:mm:ss').minutes()}min
|
||||
~{moment.duration(lesson.duration, 'minutes').humanize()}
|
||||
</Text>
|
||||
</Text>
|
||||
<Text as='span' color='gray.500'>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user