在这篇简短的文章中,我将向您展示一种优雅的方式(对我来说)消费REST API。
语境
当我们消耗REST API端点时,通常是相同的。
REST API严格对HTTP动词和终点的命名。
通常是获取项目和post / delete / patch分别创建 /删除 /编辑项目的请求。< / p>
本文仅处理以下http动词:get / post / delete / patch。您可以为您的案件大量调整代码。
我们想要什么
本文的目的是创建一个钩子,以便于轻松消耗REST API。
我们想要这样的东西(Quotes
rest API管理示例):
const { items, add, remove, edit } = useQuotes();
remove
而不是delete
,因为这是JavaScript中的保留键
此挂钩处理REST API调用和更新本地数据!
fetch 自动完成。
为了做到这一点,我们使用createResource API和Context API。
为了促进此挂钩的创建,我们创建了一个“钩子出版物”,以轻松创建许多类型的端点。
我们的“挂钩构造器”如何使用?
简单:
usecitations.tsx
import { createRESTApiHook } from "./createRESTApiHook";
export type Quote = {
id: string;
text: string;
title: string;
author?: string;
tags: string[];
numberOfVotes: number;
};
const { Provider, useRESTApi } = createRESTApiHook<Quote>();
export {
Provider as QuotesProvider,
useRESTApi as useQuotes
};
使用useQuotes
:
const { items, add, remove, edit, refetch } = useQuotes();
因为我们使用上下文:
app.tsx
<QuotesProvider baseURL="http://localhost:8080/quotes">
// Components using API
</QuotesProvider>
仅此而已!
我们的“钩子制造商”是如何创建的?
简单:
createrestapihook.tsx
import {
createContext,
createResource,
Resource,
JSXElement,
useContext,
} from "solid-js";
interface ProviderProps {
baseURL: string;
children: JSXElement;
}
export function createRESTApiHook<T>() {
interface ContextValue {
items: Resource<T[]>;
add: (item: Omit<T, "id">) => Promise<boolean>;
edit: (itemId: string, item: Partial<T>) => Promise<boolean>;
remove: (itemId: string) => Promise<boolean>;
refetch: () => void;
}
const context = createContext<ContextValue>();
function Provider(props: ProviderProps) {
const [items, { mutate, refetch }] =
createResource<T[]>(fetchItems);
async function fetchItems() {
const res = await fetch(props.baseURL);
return res.json();
}
async function add(item: Omit<T, "id">) {
try {
const res = await fetch(props.baseURL, {
method: "POST",
body: JSON.stringify(item),
headers: {
"Content-Type": "application/json",
},
});
const addedItem = await res.json();
mutate((prev) => (prev ? [...prev, addedItem] : [addedItem]));
return true;
} catch (err) {
console.error(err);
return false;
}
}
async function edit(itemId: string, item: Partial<T>) {
try {
const res = await fetch(`${props.baseURL}/${itemId}`, {
method: "PATCH",
body: JSON.stringify(item),
headers: {
"Content-Type": "application/json",
},
});
const updatedItem = await res.json();
mutate((prev) =>
prev?.map((elt: any) => (elt.id === itemId ? updatedItem : elt))
);
return true;
} catch (err) {
console.error(err);
return false;
}
}
async function remove(itemId: string) {
try {
await fetch(`${props.baseURL}/${itemId}`, {
method: "DELETE",
});
mutate((prev) => prev?.filter((elt: any) => elt.id !== itemId));
return true;
} catch (err) {
console.error(err);
return false;
}
}
const value: ContextValue = {
items,
add,
edit,
remove,
refetch,
};
return <context.Provider value={value}>{props.children}</context.Provider>;
}
function useRESTApi() {
const ctx = useContext(context);
if (!ctx) {
throw new Error("useRESTApi must be used within a RestAPIProvider");
}
return ctx;
}
return {
Provider,
useRESTApi
}
}
结论
您已经可以在应用程序中将此“挂钩概要”放在。
当然,可以改进或适应此功能。
例如,如果您的项目的ID字段不是id
,则可以将其他东西放置或通过道具适应。
感谢您的阅读!