From 35ded5385266a364ddb8ff61c7092aaaec9934d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20=C4=86wirko?= Date: Sun, 4 Sep 2022 11:46:02 +0200 Subject: [PATCH] additional hook --- CHANGELOG.md | 3 ++ README.md | 25 ++++++++++++--- hooks/core/useApiCall.tsx | 67 +++++++++++++++++++++++++++++++++++++++ package.json | 2 +- utils/apiCall.ts | 8 ++--- 5 files changed, 95 insertions(+), 10 deletions(-) create mode 100644 hooks/core/useApiCall.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 81c3e4a..52bef2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +### [2.1.0](https://github.com/ElrondDevGuild/nextjs-dapp-template/releases/tag/v2.1.0) (2022-09-04) +- new `useApiCall` hook, check the readme for more info + ### [2.0.1](https://github.com/ElrondDevGuild/nextjs-dapp-template/releases/tag/v2.0.1) (2022-08-13) - fix problems with rerendering (wrong memo usage) - update dependencies diff --git a/README.md b/README.md index 7ea0ef1..c4cca5c 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ It has straightforward and complete functionality. ### Main assumption for the dapp: - it works on Nextjs -- it uses erdjs 11.\* without the dapp-core library. +- it uses erdjs 11.* without the dapp-core library. - it uses backed side redirections to hide the API endpoint. The only exposed one is `/api/elrond` and it is used only be the dapp internally - it uses the .env file - there is an example in the repo (for all configuration, also for the demo config) - it uses chakra-ui @@ -149,9 +149,7 @@ const { isLoading, // pending state for initial load isValidating, // pending state for each revalidation of the data, for example using the mutate error, -} = useScQuery < -number > -{ +} = useScQuery({ type: SCQueryType.NUMBER, // can be number, string or boolean payload: { scAddress: process.env.NEXT_PUBLIC_MINT_SMART_CONTRACT_ADDRESS, @@ -159,7 +157,7 @@ number > args: [], }, autoInit: false, // you can enable or disable the trigger of the query on the component mount -}; +}); ``` #### useLoggingIn() @@ -186,6 +184,23 @@ The hook will provide the information about the user's auth data state. The data const { loginMethod, expires, loginToken, signature } = useLoginInfo(); ``` +#### useApiCall() + +The hook provides a convenient way of doing custom API calls unrelated to transactions or smart contract queries. By default it will use Elrond API endpoint. But it can be any type of API, not only Elrond API. In that case you would need to pass the `{ baseEndpoint: "https://some-api.com" }` in options + +```jsx +const { data, isLoading, isValidating, fetch, error } = useApiCall({ + url: `/accounts//tokens`, // can be any API endpoint without the host, because it is already handled internally + autoInit: true, // similar to useScQuery + type: 'get', // can be get, post, delete, put + payload: {}, + options: {} +}); +``` + +You can pass the response type. Returned object is the same as in `useScQuery` +The hook uses `swr` and native `fetch` under the hood. + ### Working with the API The API endpoint is proxied on the backend side. The only public API endpoint is `/api/elrond`. This is useful when you don't want to show the API endpoint because, for example, you use the paid ones. Also, there is an option to block the `/api/elrond` endpoint to be used only within the Dapp, even previewing it in the browser won't be possible. diff --git a/hooks/core/useApiCall.tsx b/hooks/core/useApiCall.tsx new file mode 100644 index 0000000..850db8b --- /dev/null +++ b/hooks/core/useApiCall.tsx @@ -0,0 +1,67 @@ +import useSWR from 'swr'; +import useSwrMutation from 'swr/mutation'; +import { apiCall } from '../../utils/apiCall'; + +interface ApiCallData { + url: string; + type?: string; + payload?: Record; + options?: Record; + autoInit?: boolean; +} + +interface FetcherArgs { + url: string; + type?: string; + payload: Record | undefined; +} + +export async function fetcher({ url, type, payload }: FetcherArgs) { + if (type === 'post') { + return await apiCall.post(url, payload || {}); + } + if (type === 'put') { + return await apiCall.put(url, payload || {}); + } + if (type === 'delete') { + return await apiCall.delete(url); + } + return await apiCall.get(url); +} + +export function useApiCall({ + url, + type, + payload, + options, + autoInit = true, +}: ApiCallData) { + const { data, error, mutate, isValidating, isLoading } = useSWR( + autoInit ? { url, payload, type } : null, + fetcher, + { + revalidateIfStale: true, + revalidateOnFocus: false, + revalidateOnReconnect: true, + ...options, + } + ); + + const { + data: mutationData, + error: mutationError, + trigger, + isMutating, + } = useSwrMutation({ url, payload, type }, fetcher, { + populateCache: true, + revalidate: true, + }); + + return { + data: (data || mutationData) as T, + isLoading: isLoading, + isValidating: isValidating || isMutating, + error: error || mutationError, + fetch: autoInit ? mutate : trigger, + }; +} diff --git a/package.json b/package.json index 66fee30..6464a98 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nextjs-dapp-template", - "version": "2.0.1", + "version": "2.1.0", "author": "Julian Ćwirko ", "license": "MIT", "homepage": "https://github.com/ElrondDevGuild/nextjs-dapp-template", diff --git a/utils/apiCall.ts b/utils/apiCall.ts index d1837bd..e4fb918 100644 --- a/utils/apiCall.ts +++ b/utils/apiCall.ts @@ -14,7 +14,7 @@ export const apiCall = { }; const response = await fetch( - this.baseEndpoint + endpoint, + (options?.baseEndpoint || this.baseEndpoint) + endpoint, Object.assign(defaultOptions, options || {}) ); @@ -45,7 +45,7 @@ export const apiCall = { }; const response = await fetch( - this.baseEndpoint + endpoint, + (options?.baseEndpoint || this.baseEndpoint) + endpoint, Object.assign(defaultOptions, options || {}) ); @@ -75,7 +75,7 @@ export const apiCall = { }; const response = await fetch( - this.baseEndpoint + endpoint, + (options?.baseEndpoint || this.baseEndpoint) + endpoint, Object.assign(defaultOptions, options || {}) ); @@ -100,7 +100,7 @@ export const apiCall = { }; const response = await fetch( - this.baseEndpoint + endpoint, + (options?.baseEndpoint || this.baseEndpoint) + endpoint, Object.assign(defaultOptions, options || {}) );