reactjs - 我想为后端的任何请求(GET、POST、PUT、DELETE)构建一个 React - Typescript 自定义钩子
问题描述
我正在构建一个全栈练习应用程序,我的一些对象包括例程、练习、练习集等。我没有为每个资源写出特定的获取、发布、放置和删除请求,而是想知道是否有更动态的方法来使用自定义 React 钩子发出请求。我见过很多 useFetch 钩子的例子,但对于其他请求类型来说并不多。我已经构建了一个自定义钩子,并创建了一个在我的组件中发出请求的策略。这似乎可行,但如果他们愿意,或者如果他们可以展示一些我正在努力实现的目标,我希望从一些更有经验的开发人员那里获得一些反馈。谢谢!
类型定义
import { AxiosError, AxiosResponse } from 'axios'
import React from 'react'
export type StatusType = "fetching" | "creating" | "updating" | "deleting" | "done"
export interface DataReturnType {
response: AxiosResponse | undefined
status: StatusType
error: AxiosError | undefined
}
// Every time there's a new resource added to my back end, all I have to do is add a
// new endpoint string here and I automatically get all the requests.
export type ResourceEndpointType = "routines" | "routines/routine" | "routines/weeks" | "set-groups" | "exercise-sets" | "exercises" | "users"
// Below essentially tells you how the request is built every time you need it.
// TypeScript makes it easy to implement because you never have to remember what the endpoint is,
// it just pops up as you're typing. Furthermore, this approach imposes more consistent structure to requests.
export type RequestObjType = {
resourceEndPoint: ResourceEndpointType
parameter?: string
query?: string
request: "get" | "post" | "put" | "delete"
body?: object
trigger:boolean
setTrigger: React.Dispatch<React.SetStateAction<boolean>>
}
钩子
import { useEffect, useState } from "react";
import { AxiosError, AxiosResponse } from "axios";
import axiosWithAuth from "../utils/axiosWithAuth"; // an instance of axios with baseUrl and appropriate headers
import { DataReturnType, RequestObjType, StatusType } from "./apiTypes";
enum Statuses {
get = "fetching",
post = "creating",
put = "updating",
delete = "deleting",
done = "done",
}
const useRequest = (requestObj: RequestObjType): DataReturnType => {
const [response, responseSet] = useState<AxiosResponse | undefined>(
undefined
);
const [status, statusSet] = useState<StatusType>(Statuses.done);
const [error, errorSet] = useState<AxiosError | undefined>(undefined);
let url = requestObj.resourceEndPoint;
if (requestObj.parameter) url += "/" + requestObj.parameter;
if (requestObj.query) url += "?" + requestObj.query;
useEffect(() => {
if (requestObj.trigger) { // trigger and setTrigger are passed in by the component
console.log(`Requesting ${requestObj.request.toUpperCase()}...`);
statusSet(Statuses[requestObj.request]);
axiosWithAuth()
[requestObj.request](url, requestObj.body)
.then((response: AxiosResponse) => {
requestObj.setTrigger(false);
responseSet(response);
statusSet("done");
})
.catch((error: AxiosError) => {
console.log(error);
errorSet(error);
statusSet("done");
requestObj.setTrigger(false);
});
}
// resetting response and error every time will allow for conditional checks in the component
responseSet(undefined);
errorSet(undefined);
requestObj.setTrigger(false);
}, [url, requestObj]);
return {
response,
status,
error,
};
};
export default useRequest;
在搜索栏组件中使用 GET 钩子
const [search, setSearch] = useState("");
const { loadExercises, paginationSet } = useExerciseContext(); // custom hook built from useContext
const [trigger, shouldFetch] = useState(false);
const { response, status } = useRequest({
request: "get",
query: `name=${search}`,
resourceEndPoint: "exercises",
trigger,
setTrigger: shouldFetch,
});
// watching for changes in the response
useEffect(() => {
if (response) {
loadExercises(response.data.data);
paginationSet(response.data.pagination)
}
}, [loadExercises, response, status, paginationSet]);
...
<Button
onClick={() => shouldFetch(true)} // triggering the request
className={classes.submitBtn}
variant="outlined"
>
Submit
</Button>
在另一个组件中使用 PUT 挂钩
const { changeRoutineColor, routineColorMap, replaceRoutine } =
useRoutinesContext();
const routineColor = routineColorMap[routine._id];
const classes = useItemStyles({ routineColor });
const [shouldUpdate, setShouldUpdate] = useState(false); // you can make the triggers more semantic
const { response, status, error } = useUpdate({ // being more semantic with the useResource import
resourceEndPoint: "routines/routine",
parameter: routine._id,
query: "select=color", // I don't need the entire object back from the backend (MongoDB/Node/Mongoose) so I can add a select to the query
request: "put",
trigger: shouldUpdate,
setTrigger: setShouldUpdate,
body: { color: routineColor },
});
// handling the response
useEffect(() => {
if (response && response.data) {
replaceRoutine({ ...routine, color: response.data.data.color });
}
}, [replaceRoutine, response, routine, error]);
const onChange = (newColor: string) => {
changeRoutineColor(routine._id, newColor);
};
//
const handleClose = () => {
popupState.close();
setShouldUpdate(true); // triggering the request
};
解决方案
推荐阅读
- reactjs - 尝试将数据发布到 Google 工作表时 OAuth 2 身份验证失败
- css - 为什么我的 CSS 手风琴不起作用?初学者在这里
- scikit-learn - Adaboost Sklearn 特征重要性 NaN
- docusignapi - DocuSign API 计划、合作伙伴集成、疑问
- tree - 这是旋转下面的 AVL 树的最佳方法吗?
- dask - Dask Dataframe:删除A列的重复项,保留B列中具有最高值的行
- flutter - 在颤动中使用长列表时单击它时如何更改列表视图项目文本
- c++ - C ++中的通用多功能组合/流水线
- jquery - 通过按钮类和文本更改在按钮单击时发送 AJAX 帖子
- c# - WinUI 3.0 桌面 - C# 页面导航