I have written a data source reusable hook pattern based on react typescript to use for container presentation pattern components. The hook uses two generic interfaces, IDataList for the data list and IData for updating
The hook code:
import axios from "axios";
import { useEffect, useState } from "react";
import { AxiosError } from "axios";
type ArrayFields = {
[FieldName: string]: string | number
}
export type FieldProps = {
[FieldName: string]: string | number | boolean | ArrayFields[],
}
export const useDataSourceListContainer = <IDataList extends FieldProps, IData extends FieldProps>(
dataSourceUrl: string,
dataSourceName: string,
dataSourceIdName: string,
postUrl: string,
putUrl: string,
getUrl: string,
) => {
const toCapital = (str: string) => str.charAt(0).toUpperCase() + str.slice(1);
const [dataSource, setDataSource] = useState<IDataList[]>([]);
const [isLoading, setIsLoading] = useState(false);
const getDataSource = async () => {
setIsLoading(true);
const response = await axios.get(`${dataSourceUrl}/${getUrl}`);
setIsLoading(false);
if (!response.data) return;
setDataSource(response.data);
};
useEffect(() => {
getDataSource();
}, [dataSourceUrl, getUrl]);
const onPost = async (data: IData) => {
try {
const res = await axios.post(`${dataSourceUrl}/${postUrl}`, {
[dataSourceName]: data,
});
setDataSource((cur) => [...cur!, { ...data, ...res.data }]);
} catch (error) {
const err = error as AxiosError
console.log(err)
}
};
const onPut = async (data: IData) => {
try {
const res = await axios.put(`${dataSourceUrl}/${putUrl}`, { [dataSourceName]: data });
setDataSource((cur) =>
cur?.map((item) =>
item[dataSourceIdName] != data[dataSourceIdName] ? item : { ...data, ...res.data }
)
);
} catch (error) {
const err = error as AxiosError
console.log(err)
}
};
const dataSourceProps = {
dataSource,
isLoading,
[`onDelete${toCapital(dataSourceName)}`]: onDelete,
[`onPut${toCapital(dataSourceName)}`]: onPut,
[`onPost${toCapital(dataSourceName)}`]: onPost,
};
return { ...dataSourceProps };
};
I deployed it for implementing the User component which shows a list of users with options of adding new users or updating users and its menu access control data
The User component code:
import { useEffect, useMemo, useRef, useState } from "react";
import UserList from "./UserList";
import { useDataSourceListContainer } from "../hooks/useDataSourceListContainer";
export type MnuAcsT={
srl:number,
mnu_title:string,
route_srl:number,
}
export type UserT={
userid:string,
userpass:string,
remarks:string,
email:string,
admin_flag: string,
mnuAcs: MnuAcsT[],
}
export type UserListT={
userid:string,
remarks:string,
email:string,
admin_flag: string,
}
export default function User() {
const { dataSource, isLoading, onPutUser, onPostUser } =
useDataSourceListContainer<UserListT,UserT>(
"/api/user",
"user",
"userid",
"newUser",
"updUser",
"users"
);
return (
<div>
<UserList
isLoading={isLoading}
listItems={dataSource}
onPut={onPutUser}
onPost={onPostUser}
/>
</div>
);
}
The following error on assigning the onput and onpost props: Type 'boolean | UserListT[] | ((srl: string | number) => Promise) | ((data: UserT) => Promise)' is not assignable to type '(data: UserT) => void'. Type 'boolean' is not assignable to type '(data: UserT) => void'.ts(2322) UserList.tsx(52, 46): The expected type comes from property 'onPut' which is declared here on type 'IntrinsicAttributes & Props'
The UserList component code:
import { useState } from "react";
import UserForm from "./UserForm";
import Table from "../components/Table";
import { UserT, UserListT } from "./User";
const userInit = {
userid: "",
userpass: "",
remarks: "",
email: "",
admin_flag: "N",
mnuAcs: [],
};
type Props={
listItems:UserListT[] , isLoading:boolean, onPut:((data: UserT) => void), onPost:((data: UserT) => void)
}
export default function UserList({ listItems, isLoading, onPut, onPost }:Props) {
....
const [userData, setUserData] = useState<UserT>(userInit);
const [mnuAcs, setMnuAcs] = useState([]);
const onSubmit = () => {
try {
if (!editMode) {
onPost({
...userData,
mnuAcs,
});
} else {
onPut({
...userData,
mnuAcs,
});
}
} catch (err) {
console.log("error happend on saving data", err);
}
};
return (
<div>
....
<UserForm
userData={userData}
mnuAcs={mnuAcs}
setMnuAcs={setMnuAcs}
onChange={onChange}
onSubmit={onSubmit}
editMode={editMode}
onClose={() => setOpenUserDlg(false)}
/>
....
<Table
newClick={() => {
setUserData(userInit);
setEditMode(false);
setOpenUserDlg(true);
}}
listItems={listItems}
....
/>
)}
</div>
);
}
I have written a data source reusable hook pattern based on react typescript to use for container presentation pattern components. The hook uses two generic interfaces, IDataList for the data list and IData for updating
The hook code:
import axios from "axios";
import { useEffect, useState } from "react";
import { AxiosError } from "axios";
type ArrayFields = {
[FieldName: string]: string | number
}
export type FieldProps = {
[FieldName: string]: string | number | boolean | ArrayFields[],
}
export const useDataSourceListContainer = <IDataList extends FieldProps, IData extends FieldProps>(
dataSourceUrl: string,
dataSourceName: string,
dataSourceIdName: string,
postUrl: string,
putUrl: string,
getUrl: string,
) => {
const toCapital = (str: string) => str.charAt(0).toUpperCase() + str.slice(1);
const [dataSource, setDataSource] = useState<IDataList[]>([]);
const [isLoading, setIsLoading] = useState(false);
const getDataSource = async () => {
setIsLoading(true);
const response = await axios.get(`${dataSourceUrl}/${getUrl}`);
setIsLoading(false);
if (!response.data) return;
setDataSource(response.data);
};
useEffect(() => {
getDataSource();
}, [dataSourceUrl, getUrl]);
const onPost = async (data: IData) => {
try {
const res = await axios.post(`${dataSourceUrl}/${postUrl}`, {
[dataSourceName]: data,
});
setDataSource((cur) => [...cur!, { ...data, ...res.data }]);
} catch (error) {
const err = error as AxiosError
console.log(err)
}
};
const onPut = async (data: IData) => {
try {
const res = await axios.put(`${dataSourceUrl}/${putUrl}`, { [dataSourceName]: data });
setDataSource((cur) =>
cur?.map((item) =>
item[dataSourceIdName] != data[dataSourceIdName] ? item : { ...data, ...res.data }
)
);
} catch (error) {
const err = error as AxiosError
console.log(err)
}
};
const dataSourceProps = {
dataSource,
isLoading,
[`onDelete${toCapital(dataSourceName)}`]: onDelete,
[`onPut${toCapital(dataSourceName)}`]: onPut,
[`onPost${toCapital(dataSourceName)}`]: onPost,
};
return { ...dataSourceProps };
};
I deployed it for implementing the User component which shows a list of users with options of adding new users or updating users and its menu access control data
The User component code:
import { useEffect, useMemo, useRef, useState } from "react";
import UserList from "./UserList";
import { useDataSourceListContainer } from "../hooks/useDataSourceListContainer";
export type MnuAcsT={
srl:number,
mnu_title:string,
route_srl:number,
}
export type UserT={
userid:string,
userpass:string,
remarks:string,
email:string,
admin_flag: string,
mnuAcs: MnuAcsT[],
}
export type UserListT={
userid:string,
remarks:string,
email:string,
admin_flag: string,
}
export default function User() {
const { dataSource, isLoading, onPutUser, onPostUser } =
useDataSourceListContainer<UserListT,UserT>(
"/api/user",
"user",
"userid",
"newUser",
"updUser",
"users"
);
return (
<div>
<UserList
isLoading={isLoading}
listItems={dataSource}
onPut={onPutUser}
onPost={onPostUser}
/>
</div>
);
}
The following error on assigning the onput and onpost props: Type 'boolean | UserListT[] | ((srl: string | number) => Promise) | ((data: UserT) => Promise)' is not assignable to type '(data: UserT) => void'. Type 'boolean' is not assignable to type '(data: UserT) => void'.ts(2322) UserList.tsx(52, 46): The expected type comes from property 'onPut' which is declared here on type 'IntrinsicAttributes & Props'
The UserList component code:
import { useState } from "react";
import UserForm from "./UserForm";
import Table from "../components/Table";
import { UserT, UserListT } from "./User";
const userInit = {
userid: "",
userpass: "",
remarks: "",
email: "",
admin_flag: "N",
mnuAcs: [],
};
type Props={
listItems:UserListT[] , isLoading:boolean, onPut:((data: UserT) => void), onPost:((data: UserT) => void)
}
export default function UserList({ listItems, isLoading, onPut, onPost }:Props) {
....
const [userData, setUserData] = useState<UserT>(userInit);
const [mnuAcs, setMnuAcs] = useState([]);
const onSubmit = () => {
try {
if (!editMode) {
onPost({
...userData,
mnuAcs,
});
} else {
onPut({
...userData,
mnuAcs,
});
}
} catch (err) {
console.log("error happend on saving data", err);
}
};
return (
<div>
....
<UserForm
userData={userData}
mnuAcs={mnuAcs}
setMnuAcs={setMnuAcs}
onChange={onChange}
onSubmit={onSubmit}
editMode={editMode}
onClose={() => setOpenUserDlg(false)}
/>
....
<Table
newClick={() => {
setUserData(userInit);
setEditMode(false);
setOpenUserDlg(true);
}}
listItems={listItems}
....
/>
)}
</div>
);
}
Share
Improve this question
edited Feb 15 at 21:11
Drew Reese
203k17 gold badges237 silver badges268 bronze badges
asked Feb 15 at 15:15
Mohammad MomtazMohammad Momtaz
6359 silver badges14 bronze badges
1 Answer
Reset to default 0the problem was with the following code:
[`onDelete${toCapital(dataSourceName)}`]: onDelete,
[`onPut${toCapital(dataSourceName)}`]: onPut,
[`onPost${toCapital(dataSourceName)}`]: onPost,
which causes the loss of the signature, so sending back the props as it is and changing that part of the code as follows:
const dataSourceProps = {
dataSource,
isLoading,
onDelete,
onPut,
onPost,
};
return { ...dataSourceProps };