Fix asynchronous error

This commit is contained in:
2023-11-05 12:25:01 +07:00
parent e82fb39787
commit b0dd3abf0e
8 changed files with 127 additions and 84 deletions
@@ -3,19 +3,15 @@ import { forwardRef } from 'react';
// Styled // Styled
import { StyledBody, StyledDialog, StyledTitle } from './styled'; import { StyledBody, StyledDialog, StyledTitle } from './styled';
export interface IDialogProps<T> { export interface IDialogProps {
title?: string; title?: string;
children?: JSX.Element[] | JSX.Element; children?: JSX.Element[] | JSX.Element;
onClose?: () => void; onClose?: () => void;
reload?: boolean; ref?: React.MutableRefObject<HTMLDialogElement | null>;
setReload?: React.Dispatch<React.SetStateAction<boolean>>;
ref?: React.MutableRefObject<HTMLDialogElement | undefined>;
data?: T | null;
isAdd?: boolean;
} }
const Dialog = forwardRef((props, ref) => { const Dialog = forwardRef((props: IDialogProps, ref) => {
const { title, children, onClose } = props as IDialogProps<unknown>; const { title, children, onClose } = props;
return ( return (
<StyledDialog <StyledDialog
ref={ref as React.LegacyRef<HTMLDialogElement> | undefined} ref={ref as React.LegacyRef<HTMLDialogElement> | undefined}
@@ -25,6 +21,6 @@ const Dialog = forwardRef((props, ref) => {
<StyledBody>{children}</StyledBody> <StyledBody>{children}</StyledBody>
</StyledDialog> </StyledDialog>
); );
}) as React.FC<IDialogProps<unknown>>; });
export default Dialog; export default Dialog;
@@ -0,0 +1,22 @@
import { ForwardedRef, useEffect, useRef } from 'react';
const useForwardRef = <T>(
ref: ForwardedRef<T>,
initialValue: T | null = null
) => {
const targetRef = useRef<T>(initialValue);
useEffect(() => {
if (!ref) return;
if (typeof ref === 'function') {
ref(targetRef.current);
} else {
ref.current = targetRef.current;
}
}, [ref]);
return targetRef;
};
export { useForwardRef };
+15 -7
View File
@@ -1,13 +1,21 @@
import { forwardRef, useEffect } from 'react'; import { forwardRef, useEffect } from 'react';
// Components // Components
import Dialog, { IDialogProps } from '../../components/Dialog'; import Dialog from '../../components/Dialog';
import RoomForm from './Form'; import RoomForm from './Form';
// Types
import { TRoom } from '../../globals/types'; import { TRoom } from '../../globals/types';
const RoomDialog = forwardRef((props, ref) => {
interface IRoomDialog {
onClose?: () => void;
reload?: boolean;
setReload?: React.Dispatch<React.SetStateAction<boolean>>;
ref?: React.MutableRefObject<HTMLDialogElement | null>;
room?: TRoom | null;
isAdd?: boolean;
}
const RoomDialog = forwardRef((props: IRoomDialog, ref) => {
const dialogRef = ref as React.MutableRefObject< const dialogRef = ref as React.MutableRefObject<
HTMLDialogElement | undefined HTMLDialogElement | undefined
>; >;
@@ -16,7 +24,7 @@ const RoomDialog = forwardRef((props, ref) => {
onClose, onClose,
setReload, setReload,
reload, reload,
data, room,
isAdd isAdd
} = props; } = props;
@@ -42,11 +50,11 @@ const RoomDialog = forwardRef((props, ref) => {
onClose={onClose!} onClose={onClose!}
reload={reload!} reload={reload!}
setReload={setReload!} setReload={setReload!}
room={data} room={room}
isAdd={isAdd!} isAdd={isAdd!}
/> />
</Dialog> </Dialog>
); );
}) as React.FC<IDialogProps<TRoom>>; });
export default RoomDialog; export default RoomDialog;
+1 -1
View File
@@ -48,7 +48,7 @@ const Room = () => {
ref={dialogRef} ref={dialogRef}
setReload={setReload} setReload={setReload}
reload={reload} reload={reload}
data={room} room={room}
isAdd={isAdd} isAdd={isAdd}
/> />
</> </>
+16 -8
View File
@@ -1,24 +1,32 @@
import { forwardRef, useEffect } from 'react'; import { forwardRef, useEffect } from 'react';
// Components // Components
import Dialog, { IDialogProps } from '../../components/Dialog'; import Dialog from '../../components/Dialog';
import UserForm from './Form'; import UserForm from './Form';
// Types // Types
import { TUser } from '../../globals/types'; import { TUser } from '../../globals/types';
import { useForwardRef } from '../../hooks/useForwardRef';
interface IUserDialog {
onClose: () => void;
reload: boolean;
setReload: React.Dispatch<React.SetStateAction<boolean>>;
ref: React.MutableRefObject<HTMLDialogElement | null>;
user: TUser | null;
isAdd: boolean;
}
const UserDialog = forwardRef((props, ref) => { const UserDialog = forwardRef((props, ref) => {
const dialogRef = ref as React.MutableRefObject< const dialogRef = useForwardRef(ref);
HTMLDialogElement | undefined
>;
// prettier-ignore // prettier-ignore
const { const {
onClose, onClose,
setReload, setReload,
reload, reload,
data, user,
isAdd isAdd,
} = props; } = props;
useEffect(() => { useEffect(() => {
@@ -43,11 +51,11 @@ const UserDialog = forwardRef((props, ref) => {
onClose={onClose!} onClose={onClose!}
reload={reload!} reload={reload!}
setReload={setReload!} setReload={setReload!}
user={data} user={user}
isAdd={isAdd!} isAdd={isAdd!}
/> />
</Dialog> </Dialog>
); );
}) as React.FC<IDialogProps<TUser>>; }) as React.FC<IUserDialog>;
export default UserDialog; export default UserDialog;
+63 -49
View File
@@ -1,4 +1,4 @@
import { useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import { FormProvider, useForm } from 'react-hook-form'; import { FormProvider, useForm } from 'react-hook-form';
@@ -38,14 +38,14 @@ import {
REQUIRED_FIELD_ERROR, REQUIRED_FIELD_ERROR,
} from '../../constants/formValidateMessage.ts'; } from '../../constants/formValidateMessage.ts';
import { getAllRoom, updateRoomStatus } from '../../services/roomServices.ts'; import { getAllRoom, updateRoomStatus } from '../../services/roomServices.ts';
import { TUser } from '../../globals/types.ts'; import { TRoom, TUser } from '../../globals/types.ts';
import { INIT_VALUE_USER_FORM } from '../../constants/variables.ts'; import { INIT_VALUE_USER_FORM } from '../../constants/variables.ts';
interface IUserFormProp { interface IUserFormProp {
onClose: () => void; onClose: () => void;
reload: boolean; reload: boolean;
setReload: React.Dispatch<React.SetStateAction<boolean>>; setReload: React.Dispatch<React.SetStateAction<boolean>>;
user?: TUser | null; user: TUser | null;
isAdd: boolean; isAdd: boolean;
} }
@@ -64,19 +64,17 @@ const UserForm = ({
formState: { errors, isDirty, isValid }, formState: { errors, isDirty, isValid },
trigger, trigger,
} = formMethods; } = formMethods;
const [rooms, setRooms] = useState<TRoom[]>([]);
const [options, setOptions] = useState<ISelectOptions[]>(); const [options, setOptions] = useState<ISelectOptions[]>();
// Init value when edit form and load options // Init value when edit form and load options
useEffect(() => { useEffect(() => {
const load = async () => { const load = async () => {
const rooms = await getAllRoom();
const options: ISelectOptions[] = []; const options: ISelectOptions[] = [];
const tempUser = isAdd const tempUser = isAdd ? {} : { ...user };
? {}
: {...user};
// Load and set default options room // Load and set default options room
if (rooms) { if (rooms.length > 0) {
rooms.forEach((item) => { rooms.forEach((item) => {
if (!item.status || tempUser?.roomId === item.id) if (!item.status || tempUser?.roomId === item.id)
options.push({ options.push({
@@ -103,55 +101,71 @@ const UserForm = ({
}; };
load(); load();
}, [reset, user, isAdd]); }, [reset, user, isAdd, rooms]);
// Submit form // Submit form
const onSubmit = async (newUser: TUser) => { const onSubmit = useCallback(
try { async (newUser: TUser) => {
if (isAdd) { try {
// Add request if (isAdd) {
const response = await sendRequest( // Add request
USER_PATH, const response = await sendRequest(
'POST', USER_PATH,
JSON.stringify(newUser) 'POST',
); JSON.stringify(newUser)
);
if (response.statusCode === STATUS_CODE.CREATE) { if (response.statusCode === STATUS_CODE.CREATE) {
toast.success(ADD_SUCCESS); toast.success(ADD_SUCCESS);
} else {
throw new Error(errorMsg(response.statusCode, response.msg));
}
// Update room status
updateRoomStatus(newUser.roomId, true);
} else { } else {
throw new Error(errorMsg(response.statusCode, response.msg)); // Edit request
const response = await sendRequest(
USER_PATH + `/${newUser.id}`,
'PUT',
JSON.stringify(newUser)
);
if (response.statusCode == STATUS_CODE.OK) {
toast.success(EDIT_SUCCESS);
} else {
throw new Error(errorMsg(response.statusCode, response.msg));
}
// Update room status
updateRoomStatus(user!.roomId, true, newUser.roomId);
} }
// Reload table data
// Update room status setReload(!reload);
updateRoomStatus(newUser.roomId, true); } catch (error: unknown) {
} else { if (error instanceof Error) {
// Edit request toast.error(error.message);
const response = await sendRequest(
USER_PATH + `/${newUser.id}`,
'PUT',
JSON.stringify(newUser)
);
if (response.statusCode == STATUS_CODE.OK) {
toast.success(EDIT_SUCCESS);
} else {
throw new Error(errorMsg(response.statusCode, response.msg));
} }
// Update room status
updateRoomStatus(user!.roomId, true, newUser.roomId);
} }
// Reload table data
setReload(!reload);
} catch (error: unknown) {
if (error instanceof Error) {
toast.error(error.message);
}
}
reset(); reset();
onClose(); onClose();
}; },
[isAdd, onClose, reload, reset, setReload, user]
);
// Load all rooms
useEffect(() => {
const loadRoom = async () => {
const rooms = await getAllRoom();
if (rooms) {
setRooms(rooms);
}
};
loadRoom();
}, [onSubmit]);
return ( return (
<FormProvider {...formMethods}> <FormProvider {...formMethods}>
+3 -8
View File
@@ -75,11 +75,7 @@ const UserRow = ({
<div>{name}</div> <div>{name}</div>
<div>{identifiedCode}</div> <div>{identifiedCode}</div>
<div>{phone}</div> <div>{phone}</div>
<div>{ <div>{roomId ? roomId : 'None'}</div>
roomId
? roomId
: 'None'
}</div>
<Menus.Menu> <Menus.Menu>
<Menus.Toggle id={id.toString()} /> <Menus.Toggle id={id.toString()} />
@@ -116,9 +112,9 @@ const UserTable = ({
openFormDialog, openFormDialog,
setUser, setUser,
}: IUserTable) => { }: IUserTable) => {
const [users, setUsers] = useState<TUser[]>([]);
const [phoneSearch, setPhoneSearch] = useState(''); const [phoneSearch, setPhoneSearch] = useState('');
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const [users, setUsers] = useState<TUser[]>([]);
const sortByValue = searchParams.get('sortBy') const sortByValue = searchParams.get('sortBy')
? searchParams.get('sortBy')! ? searchParams.get('sortBy')!
: ''; : '';
@@ -145,8 +141,7 @@ const UserTable = ({
if (errorFetchMsg) { if (errorFetchMsg) {
console.error(errorFetchMsg); console.error(errorFetchMsg);
} }
}, [data, errorFetchMsg]); }, [data, errorFetchMsg, setUsers]);
return ( return (
<> <>
+2 -2
View File
@@ -13,7 +13,7 @@ import UserDialog from './Dialog';
import { TUser } from '../../globals/types'; import { TUser } from '../../globals/types';
const User = () => { const User = () => {
const dialogRef = useRef<HTMLDialogElement>(); const dialogRef = useRef<HTMLDialogElement>(null);
const [reload, setReload] = useState(true); const [reload, setReload] = useState(true);
const [user, setUser] = useState<TUser | null>(null); const [user, setUser] = useState<TUser | null>(null);
const [isAdd, setIsAdd] = useState(false); const [isAdd, setIsAdd] = useState(false);
@@ -48,7 +48,7 @@ const User = () => {
ref={dialogRef} ref={dialogRef}
setReload={setReload} setReload={setReload}
reload={reload} reload={reload}
data={user} user={user}
isAdd={isAdd} isAdd={isAdd}
/> />
</> </>