@slangy/react
Advanced tools
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"createCrudHook.d.ts","sourceRoot":"","sources":["../../src/hooks/createCrudHook.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAEzD;;;;;GAKG;AACH,KAAK,WAAW,CAAC,IAAI,EAAE,MAAM,SAAS,MAAM,IAAI,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAE5F;;;;;;;;;;;GAWG;AACH,KAAK,oBAAoB,CACvB,UAAU,SAAS,OAAO,EAAE,EAC5B,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,KAAK,EACL,eAAe,EACf,MAAM,SAAS,MAAM,eAAe,EACpC,OAAO,SAAS,WAAW,CAAC,eAAe,EAAE,MAAM,CAAC,IAClD;IACF,KAAK,EAAE,MAAM,SAAS,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;IAChD,MAAM,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;IAC3D,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACzD,MAAM,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;IAC3D,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,gBAAgB,KAAK,eAAe,EAAE,CAAC;IAC3D,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,EAAE,KAAK,KAAK,CAAC;CAC5C,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,QAAA,MAAM,cAAc;;;;;;;SA4FnB,CAAC;AAEF,eAAe,cAAc,CAAC"} | ||
| {"version":3,"file":"createCrudHook.d.ts","sourceRoot":"","sources":["../../src/hooks/createCrudHook.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAEzD;;;;;GAKG;AACH,KAAK,WAAW,CAAC,IAAI,EAAE,MAAM,SAAS,MAAM,IAAI,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAE5F;;;;;;;;;;;GAWG;AACH,KAAK,oBAAoB,CACvB,UAAU,SAAS,OAAO,EAAE,EAC5B,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,KAAK,EACL,eAAe,EACf,MAAM,SAAS,MAAM,eAAe,EACpC,OAAO,SAAS,WAAW,CAAC,eAAe,EAAE,MAAM,CAAC,IAClD;IACF,KAAK,EAAE,MAAM,SAAS,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;IAChD,MAAM,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;IAC3D,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACzD,MAAM,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;IAC3D,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,gBAAgB,KAAK,eAAe,EAAE,CAAC;IAC3D,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,EAAE,KAAK,KAAK,CAAC;CAC5C,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,QAAA,MAAM,cAAc;;;;;;;SAiGnB,CAAC;AAEF,eAAe,cAAc,CAAC"} |
@@ -26,2 +26,3 @@ import { useEffect, useState } from 'react'; | ||
| const { data, set, create: createInStore, update: updateInStore, remove: removeFromStore, } = store(); | ||
| const shouldFetchData = !hasLoaded && !isLoading && !error; | ||
| useEffect(() => { | ||
@@ -34,2 +35,3 @@ const fetchData = async () => { | ||
| set(transform(result)); | ||
| setHasLoaded(true); | ||
| } | ||
@@ -40,11 +42,12 @@ catch (err) { | ||
| finally { | ||
| setHasLoaded(true); | ||
| setIsLoading(false); | ||
| } | ||
| }; | ||
| if (data.length === 0 && !hasLoaded) { | ||
| fetchData(); | ||
| } | ||
| fetchData(); | ||
| // eslint-disable-next-line react-hooks/exhaustive-deps | ||
| }, [...args, set, data.length]); | ||
| }, [shouldFetchData]); | ||
| useEffect(() => { | ||
| hasLoaded && setHasLoaded(false); | ||
| // eslint-disable-next-line react-hooks/exhaustive-deps | ||
| }, [...args]); | ||
| const extraProperties = extra ? extra(data) : undefined; | ||
@@ -51,0 +54,0 @@ const hook = { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"createCrudHook.js","sourceRoot":"","sources":["../../src/hooks/createCrudHook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AA2C5C;;;;;;;;;;;GAWG;AACH,MAAM,cAAc,GAAG,CASrB,EACA,KAAK,EACL,MAAM,EACN,IAAI,EACJ,MAAM,EACN,MAAM,EACN,SAAS,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,IAAyB,EAC/C,KAAK,GAUN,EAAE,EAAE;IACH;;;;;OAKG;IACH,OAAO,SAAS,OAAO,CAAC,GAAG,IAAgB;QACzC,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QACjD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAoB,SAAS,CAAC,CAAC;QACjE,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;QAElD,MAAM,EACJ,IAAI,EACJ,GAAG,EACH,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,eAAe,GACxB,GAAG,KAAK,EAAE,CAAC;QAEZ,SAAS,CAAC,GAAG,EAAE;YACb,MAAM,SAAS,GAAG,KAAK,IAAI,EAAE;gBAC3B,YAAY,CAAC,IAAI,CAAC,CAAC;gBACnB,QAAQ,CAAC,SAAS,CAAC,CAAC;gBACpB,IAAI;oBACF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;oBACnC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;iBACxB;gBAAC,OAAO,GAAG,EAAE;oBACZ,QAAQ,CAAC,GAAY,CAAC,CAAC;iBACxB;wBAAS;oBACR,YAAY,CAAC,IAAI,CAAC,CAAC;oBACnB,YAAY,CAAC,KAAK,CAAC,CAAC;iBACrB;YACH,CAAC,CAAC;YAEF,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE;gBACnC,SAAS,EAAE,CAAC;aACb;YACD,uDAAuD;QACzD,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;QAEhC,MAAM,eAAe,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAExD,MAAM,IAAI,GAAG;YACX,IAAI,EAAE,IAAI;YACV,KAAK,EAAE,KAAK;YACZ,SAAS,EAAE,SAAS;YACpB,KAAK,CAAC,MAAM,CAAC,IAAoB;gBAC/B,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;gBACnC,aAAa,CAAC,OAAO,CAAC,CAAC;YACzB,CAAC;YACD,KAAK,CAAC,MAAM,CAAC,MAA+B;gBAC1C,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;gBACrB,eAAe,CAAC,MAAM,CAAC,CAAC;YAC1B,CAAC;YACD,KAAK,CAAC,MAAM,CAAC,IAAoB;gBAC/B,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;gBACvC,aAAa,CAAC,WAAW,CAAC,CAAC;YAC7B,CAAC;SACF,CAAC;QAEF,OAAO;YACL,GAAG,IAAI;YACP,GAAI,eAAyB;SAC9B,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,cAAc,CAAC"} | ||
| {"version":3,"file":"createCrudHook.js","sourceRoot":"","sources":["../../src/hooks/createCrudHook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AA2C5C;;;;;;;;;;;GAWG;AACH,MAAM,cAAc,GAAG,CASrB,EACA,KAAK,EACL,MAAM,EACN,IAAI,EACJ,MAAM,EACN,MAAM,EACN,SAAS,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,IAAyB,EAC/C,KAAK,GAUN,EAAE,EAAE;IACH;;;;;OAKG;IACH,OAAO,SAAS,OAAO,CAAC,GAAG,IAAgB;QACzC,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QACjD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAoB,SAAS,CAAC,CAAC;QACjE,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;QAElD,MAAM,EACJ,IAAI,EACJ,GAAG,EACH,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,eAAe,GACxB,GAAG,KAAK,EAAE,CAAC;QAEZ,MAAM,eAAe,GAAG,CAAC,SAAS,IAAI,CAAC,SAAS,IAAI,CAAC,KAAK,CAAC;QAE3D,SAAS,CAAC,GAAG,EAAE;YACb,MAAM,SAAS,GAAG,KAAK,IAAI,EAAE;gBAC3B,YAAY,CAAC,IAAI,CAAC,CAAC;gBACnB,QAAQ,CAAC,SAAS,CAAC,CAAC;gBACpB,IAAI,CAAC;oBACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;oBACnC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;oBACvB,YAAY,CAAC,IAAI,CAAC,CAAC;gBACrB,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,QAAQ,CAAC,GAAY,CAAC,CAAC;gBACzB,CAAC;wBAAS,CAAC;oBACT,YAAY,CAAC,KAAK,CAAC,CAAC;gBACtB,CAAC;YACH,CAAC,CAAC;YAEF,SAAS,EAAE,CAAC;YACZ,uDAAuD;QACzD,CAAC,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC;QAEtB,SAAS,CAAC,GAAG,EAAE;YACb,SAAS,IAAI,YAAY,CAAC,KAAK,CAAC,CAAC;YACjC,uDAAuD;QACzD,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QAEd,MAAM,eAAe,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAExD,MAAM,IAAI,GAAG;YACX,IAAI,EAAE,IAAI;YACV,KAAK,EAAE,KAAK;YACZ,SAAS,EAAE,SAAS;YACpB,KAAK,CAAC,MAAM,CAAC,IAAoB;gBAC/B,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;gBACnC,aAAa,CAAC,OAAO,CAAC,CAAC;YACzB,CAAC;YACD,KAAK,CAAC,MAAM,CAAC,MAA+B;gBAC1C,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;gBACrB,eAAe,CAAC,MAAM,CAAC,CAAC;YAC1B,CAAC;YACD,KAAK,CAAC,MAAM,CAAC,IAAoB;gBAC/B,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;gBACvC,aAAa,CAAC,WAAW,CAAC,CAAC;YAC7B,CAAC;SACF,CAAC;QAEF,OAAO;YACL,GAAG,IAAI;YACP,GAAI,eAAyB;SAC9B,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,cAAc,CAAC"} |
+9
-6
| { | ||
| "name": "@slangy/react", | ||
| "version": "2.3.0", | ||
| "version": "2.4.0", | ||
| "author": { | ||
@@ -10,2 +10,5 @@ "name": "Dani Lupión", | ||
| "type": "module", | ||
| "files": [ | ||
| "dist/**/*" | ||
| ], | ||
| "exports": { | ||
@@ -39,7 +42,7 @@ "./*": "./dist/*" | ||
| "@types/jest": "^29.5.8", | ||
| "@types/node": "^20.9.0", | ||
| "@types/node": "^20.9.2", | ||
| "@types/react": "^18.2.37", | ||
| "@typescript-eslint/eslint-plugin": "^6.11.0", | ||
| "@typescript-eslint/parser": "^6.11.0", | ||
| "eslint": "^8.53.0", | ||
| "@typescript-eslint/eslint-plugin": "^6.12.0", | ||
| "@typescript-eslint/parser": "^6.12.0", | ||
| "eslint": "^8.54.0", | ||
| "eslint-config-prettier": "^9.0.0", | ||
@@ -60,3 +63,3 @@ "eslint-config-react-app": "^7.0.1", | ||
| "swr": "^2.2.4", | ||
| "typescript": "^5.2.2", | ||
| "typescript": "^5.3.2", | ||
| "zustand": "^4.4.6" | ||
@@ -63,0 +66,0 @@ }, |
| { | ||
| "root": true, | ||
| "env": { | ||
| "browser": true, | ||
| "es2020": true | ||
| }, | ||
| "extends": [ | ||
| "eslint:recommended", | ||
| // "plugin:@typescript-eslint/recommended", | ||
| "plugin:eslint-comments/recommended", | ||
| "plugin:import/typescript", | ||
| "plugin:react/recommended", | ||
| "react-app", | ||
| "react-app/jest", | ||
| "plugin:react-hooks/recommended", | ||
| "plugin:react/jsx-runtime", | ||
| "prettier" | ||
| ], | ||
| "settings": { | ||
| "import/resolver": { | ||
| "node": { | ||
| "extensions": [".js", ".jsx", ".ts", ".tsx"] | ||
| }, | ||
| "typescript": { | ||
| "project": "./tsconfig.json" | ||
| } | ||
| }, | ||
| "react": { | ||
| "version": "detect" | ||
| } | ||
| }, | ||
| "ignorePatterns": ["dist", ".eslintrc.cjs"], | ||
| "parser": "@typescript-eslint/parser", | ||
| "plugins": ["react-refresh"], | ||
| "rules": { | ||
| "react-refresh/only-export-components": "warn", | ||
| "react/react-in-jsx-scope": "off", | ||
| "@typescript-eslint/explicit-module-boundary-types": "off", | ||
| "eslint-comments/disable-enable-pair": ["error", { "allowWholeFile": true }], | ||
| "import/no-unresolved": ["error", {}], | ||
| "import/order": [ | ||
| "error", | ||
| { | ||
| "newlines-between": "always", | ||
| "alphabetize": { "order": "asc" }, | ||
| "warnOnUnassignedImports": true | ||
| } | ||
| ], | ||
| "sort-imports": [ | ||
| "error", | ||
| { | ||
| "ignoreCase": false, | ||
| "ignoreDeclarationSort": true, | ||
| "ignoreMemberSort": false, | ||
| "memberSyntaxSortOrder": ["none", "all", "multiple", "single"] | ||
| } | ||
| ] | ||
| } | ||
| } |
| { | ||
| "printWidth": 100, | ||
| "singleQuote": true, | ||
| "trailingComma": "all" | ||
| } |
| import { useEffect, useState } from 'react'; | ||
| import { CrudStore } from '../stores/createCrudStore.js'; | ||
| /** | ||
| * A function that removes an item from a collection based on its ID. | ||
| * | ||
| * @template Item The type of the collection item. | ||
| * @template ItemId The type of the item's identifier. | ||
| */ | ||
| type CrudRemover<Item, ItemId extends keyof Item> = (itemId: Item[ItemId]) => Promise<void>; | ||
| /** | ||
| * Parameters required to create a custom CRUD hook. | ||
| * | ||
| * @template ReaderArgs The types of arguments accepted by the read function. | ||
| * @template CreateItemType The type of item being created. | ||
| * @template UpdateItemType The type of item being updated. | ||
| * @template ReaderReturnType The type of data returned from the read function. | ||
| * @template Extra Any extra properties or methods the resulting hook should have. | ||
| * @template TransformedType The type of data returned after optional transformation. | ||
| * @template ItemId The type of the item's identifier in the transformed data. | ||
| * @template Remover The type for the remove function. | ||
| */ | ||
| type CreateCrudHookParams< | ||
| ReaderArgs extends unknown[], | ||
| CreateItemType, | ||
| UpdateItemType, | ||
| ReaderReturnType, | ||
| Extra, | ||
| TransformedType, | ||
| ItemId extends keyof TransformedType, | ||
| Remover extends CrudRemover<TransformedType, ItemId>, | ||
| > = { | ||
| store: () => CrudStore<TransformedType, ItemId>; | ||
| create: (item: CreateItemType) => Promise<TransformedType>; | ||
| read: (...args: ReaderArgs) => Promise<ReaderReturnType>; | ||
| update: (item: UpdateItemType) => Promise<TransformedType>; | ||
| remove: Remover; | ||
| transform?: (data?: ReaderReturnType) => TransformedType[]; | ||
| extra?: (data: TransformedType[]) => Extra; | ||
| }; | ||
| /** | ||
| * A utility hook to create a custom CRUD hook with optional data transformation. | ||
| * | ||
| * @template ReaderArgs The types of arguments accepted by the read function. | ||
| * @template CreateItemType The type of item being created. | ||
| * @template UpdateItemType The type of item being updated. | ||
| * @template ReaderReturnType The type of data returned from the read function. | ||
| * @template Extra Any extra properties or methods the resulting hook should have. | ||
| * @template TransformedType The type of data returned after optional transformation. | ||
| * @template ItemId The type of the item's identifier in the transformed data. | ||
| * @template Remove The type for the remove function. | ||
| */ | ||
| const createCrudHook = < | ||
| ReaderArgs extends unknown[], | ||
| CreateItemType, | ||
| UpdateItemType, | ||
| ReaderReturnType, | ||
| Extra, | ||
| TransformedType = ReaderReturnType, | ||
| ItemId extends keyof TransformedType = 'id' extends keyof TransformedType ? 'id' : never, | ||
| Remove extends CrudRemover<TransformedType, ItemId> = CrudRemover<TransformedType, ItemId>, | ||
| >({ | ||
| store, | ||
| create, | ||
| read, | ||
| update, | ||
| remove, | ||
| transform = (data) => data as TransformedType[], | ||
| extra, | ||
| }: CreateCrudHookParams< | ||
| ReaderArgs, | ||
| CreateItemType, | ||
| UpdateItemType, | ||
| ReaderReturnType, | ||
| Extra, | ||
| TransformedType, | ||
| ItemId, | ||
| Remove | ||
| >) => { | ||
| /** | ||
| * Hook that provides CRUD operations and manages data state. | ||
| * | ||
| * @param args The arguments required for the read operation. | ||
| * @returns object An object containing data, loading error and CRUD operations (and fields from extra factory). | ||
| */ | ||
| return function useData(...args: ReaderArgs) { | ||
| const [isLoading, setIsLoading] = useState(true); | ||
| const [error, setError] = useState<Error | undefined>(undefined); | ||
| const [hasLoaded, setHasLoaded] = useState(false); | ||
| const { | ||
| data, | ||
| set, | ||
| create: createInStore, | ||
| update: updateInStore, | ||
| remove: removeFromStore, | ||
| } = store(); | ||
| useEffect(() => { | ||
| const fetchData = async () => { | ||
| setIsLoading(true); | ||
| setError(undefined); | ||
| try { | ||
| const result = await read(...args); | ||
| set(transform(result)); | ||
| } catch (err) { | ||
| setError(err as Error); | ||
| } finally { | ||
| setHasLoaded(true); | ||
| setIsLoading(false); | ||
| } | ||
| }; | ||
| if (data.length === 0 && !hasLoaded) { | ||
| fetchData(); | ||
| } | ||
| // eslint-disable-next-line react-hooks/exhaustive-deps | ||
| }, [...args, set, data.length]); | ||
| const extraProperties = extra ? extra(data) : undefined; | ||
| const hook = { | ||
| data: data, | ||
| error: error, | ||
| isLoading: isLoading, | ||
| async create(item: CreateItemType) { | ||
| const newItem = await create(item); | ||
| createInStore(newItem); | ||
| }, | ||
| async remove(itemId: TransformedType[ItemId]) { | ||
| await remove(itemId); | ||
| removeFromStore(itemId); | ||
| }, | ||
| async update(item: UpdateItemType) { | ||
| const updatedItem = await update(item); | ||
| updateInStore(updatedItem); | ||
| }, | ||
| }; | ||
| return { | ||
| ...hook, | ||
| ...(extraProperties as Extra), | ||
| }; | ||
| }; | ||
| }; | ||
| export default createCrudHook; |
| import useSWR, { SWRResponse } from 'swr'; | ||
| /** | ||
| * Parameters required to create a custom fetch hook. | ||
| * | ||
| * @template FetcherArgs The types of arguments accepted by the fetcher function. | ||
| * @template FetcherReturnType The type of data returned from the fetcher function. | ||
| * @template TransformedType The type of data returned from the transform function. | ||
| */ | ||
| type CreateFetchHookParams<FetcherArgs extends unknown[], FetcherReturnType, TransformedType> = { | ||
| fetcher: (...args: FetcherArgs) => Promise<FetcherReturnType>; | ||
| swrKey: string; | ||
| transform?: (data?: FetcherReturnType) => TransformedType; | ||
| }; | ||
| /** | ||
| * Return type of createFetchHook. | ||
| */ | ||
| type FetchHook<T> = { | ||
| data: T | undefined; | ||
| } & Omit<SWRResponse, 'data'>; | ||
| /** | ||
| * A utility hook to create a custom SWR hook with optional data transformation. | ||
| * | ||
| * @param fetcher The function used to fetch data. | ||
| * @param swrKey The caching key used by SWR. | ||
| * @param transform An optional function to transform or enrich the fetched data. | ||
| * @returns HookResponse A custom hook tailored to the given configuration. | ||
| */ | ||
| const createFetchHook = | ||
| <FetcherArgs extends unknown[], FetcherReturnType, TransformedType = FetcherReturnType>({ | ||
| fetcher, | ||
| swrKey, | ||
| transform = (data) => data as TransformedType, | ||
| }: CreateFetchHookParams<FetcherArgs, FetcherReturnType, TransformedType>) => | ||
| (...args: FetcherArgs): FetchHook<TransformedType> => { | ||
| const { data, ...rest } = useSWR([swrKey, args], ([, fetcherArgs]) => fetcher(...fetcherArgs)); | ||
| const transformedData = transform(data); | ||
| return { | ||
| ...rest, | ||
| data: transformedData, | ||
| }; | ||
| }; | ||
| export default createFetchHook; |
| import { create } from 'zustand'; | ||
| /** | ||
| * Return type of crudStoreFactory. | ||
| * | ||
| * @template ItemType The types of element the store holds. | ||
| * @template ItemId The id of ItemType. | ||
| */ | ||
| export type CrudStore<ItemType, ItemId extends keyof ItemType> = { | ||
| data: ItemType[]; | ||
| set: (items: ItemType[]) => void; | ||
| create: (...items: ItemType[]) => void; | ||
| remove: (idToRemove: ItemType[ItemId]) => void; | ||
| update: (updatedItem: ItemType) => void; | ||
| }; | ||
| /** | ||
| * Parameters required to create a custom crud store. | ||
| * | ||
| * @template ItemType The types of element the store holds. | ||
| * @template ItemId The id of ItemType. | ||
| */ | ||
| type CrudStoreFactoryParams<ItemType, ItemId extends keyof ItemType> = { | ||
| idKey?: ItemId; | ||
| }; | ||
| /** | ||
| * A utility hook to create a custom zustand based crud store. | ||
| * @param idKey | ||
| */ | ||
| const createCrudStore = < | ||
| ItemType, | ||
| ItemId extends keyof ItemType = 'id' extends keyof ItemType ? 'id' : never, | ||
| >({ idKey = 'id' as ItemId }: CrudStoreFactoryParams<ItemType, ItemId> = {}) => | ||
| create<CrudStore<ItemType, ItemId>>((setStore) => ({ | ||
| data: [], | ||
| set: (items: ItemType[]) => { | ||
| setStore(() => ({ data: [...items] })); | ||
| }, | ||
| create: (...items: ItemType[]) => { | ||
| setStore((state) => ({ data: [...state.data, ...items] })); | ||
| }, | ||
| update: (updatedItem: ItemType) => { | ||
| setStore((state) => ({ | ||
| data: state.data.map((item) => (item[idKey] === updatedItem[idKey] ? updatedItem : item)), | ||
| })); | ||
| }, | ||
| remove: (idToRemove: ItemType[ItemId]) => { | ||
| setStore((state) => ({ | ||
| data: state.data.filter((item) => item[idKey] !== idToRemove), | ||
| })); | ||
| }, | ||
| })); | ||
| export default createCrudStore; |
-105
| { | ||
| "compilerOptions": { | ||
| /* Visit https://aka.ms/tsconfig to read more about this file */ | ||
| /* Projects */ | ||
| // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ | ||
| // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ | ||
| // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ | ||
| // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ | ||
| // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ | ||
| // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ | ||
| /* Language and Environment */ | ||
| "target": "es2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ | ||
| "lib": [ | ||
| "esnext" | ||
| ], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ | ||
| // "jsx": "preserve", /* Specify what JSX code is generated. */ | ||
| // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ | ||
| // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ | ||
| // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ | ||
| // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ | ||
| // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ | ||
| // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ | ||
| // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ | ||
| // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ | ||
| // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ | ||
| /* Modules */ | ||
| "module": "NodeNext", /* Specify what module code is generated. */ | ||
| "rootDir": "./src", /* Specify the root folder within your source files. */ | ||
| "moduleResolution": "NodeNext", /* Specify how TypeScript looks up a file from a given module specifier. */ | ||
| "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ | ||
| // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ | ||
| // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ | ||
| // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ | ||
| // "types": [], /* Specify type package names to be included without being referenced in a source file. */ | ||
| // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ | ||
| // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ | ||
| "resolveJsonModule": true, /* Enable importing .json files. */ | ||
| // "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */ | ||
| /* JavaScript Support */ | ||
| // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ | ||
| // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ | ||
| // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ | ||
| /* Emit */ | ||
| "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ | ||
| "declarationMap": true, /* Create sourcemaps for d.ts files. */ | ||
| // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ | ||
| "sourceMap": true, /* Create source map files for emitted JavaScript files. */ | ||
| // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ | ||
| "outDir": "./dist", /* Specify an output folder for all emitted files. */ | ||
| // "removeComments": true, /* Disable emitting comments. */ | ||
| // "noEmit": true, /* Disable emitting files from a compilation. */ | ||
| // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ | ||
| // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ | ||
| // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ | ||
| // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ | ||
| // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ | ||
| // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ | ||
| // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ | ||
| // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ | ||
| // "newLine": "crlf", /* Set the newline character for emitting files. */ | ||
| // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ | ||
| // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ | ||
| // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ | ||
| // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ | ||
| // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ | ||
| // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ | ||
| /* Interop Constraints */ | ||
| // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ | ||
| "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ | ||
| "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ | ||
| // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ | ||
| "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ | ||
| /* Type Checking */ | ||
| "strict": true, /* Enable all strict type-checking options. */ | ||
| // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ | ||
| // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ | ||
| // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ | ||
| // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ | ||
| // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ | ||
| // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ | ||
| // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ | ||
| // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ | ||
| "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ | ||
| "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ | ||
| // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ | ||
| "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ | ||
| "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ | ||
| // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ | ||
| // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ | ||
| // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ | ||
| // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ | ||
| // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ | ||
| /* Completeness */ | ||
| // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ | ||
| "skipLibCheck": true /* Skip type checking all .d.ts files. */ | ||
| } | ||
| } |
18348
-52.92%13
-31.58%226
-63.25%