mirror of
https://github.com/Nezumi-2711/react-training.git
synced 2026-09-22 13:38:51 +00:00
Fix asynchronous error
This commit is contained in:
@@ -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 };
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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}>
|
||||||
|
|||||||
@@ -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 (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -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}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|||||||
Reference in New Issue
Block a user