最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

reactjs - React TypeScript Reusable and Updatable Generic Data source hook pattern ,Error on the onput and onpost props - Stack

programmeradmin2浏览0评论

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
Add a comment  | 

1 Answer 1

Reset to default 0

the 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 };

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论