From 0787120bea1b93444bcd1b759f553541baf8428b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C4=B1ld=C4=B1ray=20=C3=9Cnl=C3=BC?= Date: Thu, 12 Jan 2023 15:49:57 +0300 Subject: [PATCH 01/29] feat: add base useInfinityGetList hook --- packages/core/src/hooks/data/index.ts | 1 + .../core/src/hooks/data/useInfinityGetList.ts | 147 ++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 packages/core/src/hooks/data/useInfinityGetList.ts diff --git a/packages/core/src/hooks/data/index.ts b/packages/core/src/hooks/data/index.ts index f8a29f1dcf00..9da2d40dd53f 100644 --- a/packages/core/src/hooks/data/index.ts +++ b/packages/core/src/hooks/data/index.ts @@ -15,3 +15,4 @@ export { useCustom } from "./useCustom"; export { useCustomMutation } from "./useCustomMutation"; export { useDataProvider } from "./useDataProvider"; +export { useInfinityGetList } from "./useInfinityGetList"; diff --git a/packages/core/src/hooks/data/useInfinityGetList.ts b/packages/core/src/hooks/data/useInfinityGetList.ts new file mode 100644 index 000000000000..6443d8d2ebfb --- /dev/null +++ b/packages/core/src/hooks/data/useInfinityGetList.ts @@ -0,0 +1,147 @@ +import { + QueryObserverResult, + UseQueryOptions, + useInfiniteQuery, + UseInfiniteQueryOptions, + InfiniteQueryObserverResult, +} from "@tanstack/react-query"; +import { + GetListResponse, + CrudFilters, + Pagination, + BaseRecord, + HttpError, + CrudSorting, + MetaDataQuery, + SuccessErrorNotification, + LiveModeProps, +} from "../../interfaces"; +import { + useResource, + useCheckError, + useHandleNotification, + useResourceSubscription, + useTranslate, + useDataProvider, +} from "@hooks"; +import { queryKeys, pickDataProvider } from "@definitions/helpers"; + +export interface UseListConfig { + pagination?: Pagination; + hasPagination?: boolean; + sort?: CrudSorting; + filters?: CrudFilters; +} + +export type UseInfinityListProps = { + /** + * Resource name for API data interactions + */ + resource: string; + /** + * Configuration for pagination, sorting and filtering + * @type [`UseListConfig`](/docs/api-reference/core/hooks/data/useList/#config-parameters) + */ + config?: UseListConfig; + /** + * react-query's [useQuery](https://tanstack.com/query/v4/docs/react/reference/useInfiniteQuery) options, + */ + queryOptions?: UseInfiniteQueryOptions, TError>; + /** + * Metadata query for `dataProvider` + */ + metaData?: MetaDataQuery; + /** + * If there is more than one `dataProvider`, you should use the `dataProviderName` that you will use. + */ + dataProviderName?: string; +} & SuccessErrorNotification & + LiveModeProps; + +/** + * `useList` is a modified version of `react-query`'s {@link https://react-query.tanstack.com/guides/queries `useQuery`} used for retrieving items from a `resource` with pagination, sort, and filter configurations. + * + * It uses the `getList` method as the query function from the `dataProvider` which is passed to ``. + * + * @see {@link https://refine.dev/docs/core/hooks/data/useList} for more details. + * + * @typeParam TData - Result data of the query extends {@link https://refine.dev/docs/core/interfaceReferences#baserecord `BaseRecord`} + * @typeParam TError - Custom error object that extends {@link https://refine.dev/docs/core/interfaceReferences#httperror `HttpError`} + * + */ +export const useInfinityGetList = < + TData extends BaseRecord = BaseRecord, + TError extends HttpError = HttpError, +>({ + resource, + config, + queryOptions, + successNotification, + errorNotification, + metaData, + liveMode, + onLiveEvent, + liveParams, + dataProviderName, +}: UseInfinityListProps): InfiniteQueryObserverResult< + GetListResponse, + TError +> => { + const { resources } = useResource(); + const dataProvider = useDataProvider(); + const queryKey = queryKeys( + resource, + pickDataProvider(resource, dataProviderName, resources), + metaData, + ); + const { getList } = dataProvider( + pickDataProvider(resource, dataProviderName, resources), + ); + + const translate = useTranslate(); + const { mutate: checkError } = useCheckError(); + const handleNotification = useHandleNotification(); + + const isEnabled = + queryOptions?.enabled === undefined || queryOptions?.enabled === true; + + useResourceSubscription({ + resource, + types: ["*"], + params: { + metaData, + pagination: config?.pagination, + hasPagination: config?.hasPagination, + sort: config?.sort, + filters: config?.filters, + subscriptionType: "useList", + ...liveParams, + }, + channel: `resources/${resource}`, + enabled: isEnabled, + liveMode, + onLiveEvent, + }); + + const queryResponse = useInfiniteQuery, TError>( + queryKey.list(config), + ({ queryKey, pageParam, signal }) => { + const { hasPagination, ...restConfig } = config || {}; + return getList({ + resource, + ...restConfig, + hasPagination, + metaData: { + ...metaData, + queryContext: { + queryKey, + pageParam, + signal, + }, + }, + }); + }, + ); + + return queryResponse; +}; From e4e51139c23223311e0a968b783ea3ace4423499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C4=B1ld=C4=B1ray=20=C3=9Cnl=C3=BC?= Date: Fri, 13 Jan 2023 13:15:43 +0300 Subject: [PATCH 02/29] chore: add base example app --- examples/infinity-list/.gitignore | 23 +++++++++ examples/infinity-list/README.md | 45 +++++++++++++++++ examples/infinity-list/package.json | 39 +++++++++++++++ examples/infinity-list/public/favicon.ico | Bin 0 -> 101076 bytes examples/infinity-list/public/index.html | 44 ++++++++++++++++ examples/infinity-list/public/manifest.json | 15 ++++++ examples/infinity-list/src/App.css | 29 +++++++++++ examples/infinity-list/src/App.tsx | 23 +++++++++ examples/infinity-list/src/index.tsx | 13 +++++ .../infinity-list/src/interfaces/index.d.ts | 6 +++ .../infinity-list/src/pages/posts/list.tsx | 47 ++++++++++++++++++ examples/infinity-list/src/react-app-env.d.ts | 1 + examples/infinity-list/tsconfig.json | 27 ++++++++++ 13 files changed, 312 insertions(+) create mode 100644 examples/infinity-list/.gitignore create mode 100644 examples/infinity-list/README.md create mode 100644 examples/infinity-list/package.json create mode 100644 examples/infinity-list/public/favicon.ico create mode 100644 examples/infinity-list/public/index.html create mode 100644 examples/infinity-list/public/manifest.json create mode 100644 examples/infinity-list/src/App.css create mode 100644 examples/infinity-list/src/App.tsx create mode 100644 examples/infinity-list/src/index.tsx create mode 100644 examples/infinity-list/src/interfaces/index.d.ts create mode 100644 examples/infinity-list/src/pages/posts/list.tsx create mode 100644 examples/infinity-list/src/react-app-env.d.ts create mode 100644 examples/infinity-list/tsconfig.json diff --git a/examples/infinity-list/.gitignore b/examples/infinity-list/.gitignore new file mode 100644 index 000000000000..4d29575de804 --- /dev/null +++ b/examples/infinity-list/.gitignore @@ -0,0 +1,23 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/examples/infinity-list/README.md b/examples/infinity-list/README.md new file mode 100644 index 000000000000..573fba82a078 --- /dev/null +++ b/examples/infinity-list/README.md @@ -0,0 +1,45 @@ + +
+ + + +
+
+ + +
+ +
+ +
Build your React-based CRUD applications, without constraints.
An open source, headless web application framework developed with flexibility in mind. + +
+
+ + +[![Discord](https://img.shields.io/discord/837692625737613362.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/refine) +[![Twitter Follow](https://img.shields.io/twitter/follow/refine_dev?style=social)](https://twitter.com/refine_dev) + +refine - 100% open source React framework to build web apps 3x faster | Product Hunt + +
+ +## Try it out on your local + +```bash +npm create refine-app@latest -- --example base-headless +``` + +## Try it out on StackBlitz + +
+ +[![Open base-headless example from refine](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/refinedev/refine/tree/master/examples/base-headless?terminal=start&preset=node) + diff --git a/examples/infinity-list/package.json b/examples/infinity-list/package.json new file mode 100644 index 000000000000..5f1182b67673 --- /dev/null +++ b/examples/infinity-list/package.json @@ -0,0 +1,39 @@ +{ + "name": "infinity-list", + "version": "3.25.0", + "private": true, + "dependencies": { + "@pankod/refine-core": "^3.94.2", + "@pankod/refine-cli": "^1.17.0", + "@pankod/refine-react-hook-form": "^3.37.2", + "@pankod/refine-react-router-v6": "^3.38.0", + "@pankod/refine-react-table": "^4.9.0", + "@pankod/refine-simple-rest": "^3.37.4", + "@types/node": "^12.20.11", + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", + "react": "^18.0.0", + "react-dom": "^18.0.0", + "react-scripts": "^5.0.0", + "cross-env": "^7.0.3", + "typescript": "^4.7.4" + }, + "scripts": { + "start": "cross-env DISABLE_ESLINT_PLUGIN=true react-scripts start", + "build": "cross-env DISABLE_ESLINT_PLUGIN=true react-scripts build", + "eject": "react-scripts eject", + "refine": "refine" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/examples/infinity-list/public/favicon.ico b/examples/infinity-list/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..2f05c5f484fbf720523541519ff0cad4df752a9f GIT binary patch literal 101076 zcmeHQe{dAl9p5Mx285uHAE#mfD^W2m9Z*1oq-v|q2soYoQE3J0P_RR*R0i#gO+Xn; zEnrA9Q|Tx{MFmGiTUrn)$&CVvL`5hdgcwjzC;_8}Ku9jz?|1KZ@9l>D-d)~h@Al<; zlTUW{zWerlzn{;$z1z3n_g2#~wC`#7%FqUDGcVG#nXtZKL3sO`UKv^qyjN5d*}g^7 zn)+sFBSwU`*Icb>SKJLH2S>L1Yuc818Cri>;_>e9p=rN+B13!f?n$@z?R{BqICS4T z@3`$}XhrnGs(XUsZ%&e|kf8-nl`I)#JuCU8t2_ zrS%^c-{1N{=Q5tE%d9_`_xFYkZ|wUzzqMlbym@CYIrYqvGsVSY_OHBmYIS8pVb#W^ zr$(-sd%=Pc8S(zTx$f4&;6KJ~+%$D%L*83|Xq{CNT2(k--@Ls`Q zp`Sk4bJ~iAeN#r4wvNizdJTyCZ%2L4#!qLrp5EU6`KEtQY~EMde(1r9P^j>&?_S+r zGH&zontglnC!G6YhJN(*)oUj;@2fv}`}{2r)E%7p!$a?n%_v%~#ryY*_6Z9@_l+O1 zf92HAKF)7FxohjViqM8>b!XZdTB-xZc_qKBoi?-ebj^(FAMCw#RcOj1PqeQ3vWNET z3*!E3Y1JluwD(wRTYcN3k3RHIy=~#;Z`{>9y0Ya&ZAGYM^Nwi~n;$7GK5`i1y65AG z&9gr&nLlq*eENo)!QZMgr~j|vg9F1_Kd#rmd1-rlUiF{aR+hH4?JL9b=i_aD}ts7q2-ectM z*8VSd!j;DcwVpX#7HT>1tTyPyBKHucFpP};iV z-O%yprv-nM(5GX>nT*#?zj3(z@bHy$dTsQ%+=7e6zKxWOQ50 z#bXlr7<_t6pE>K)xf87q{!|aHpSrx}OiSKl8-{%`dFsJ~J?9i9;w79TZLh}h-{y@O zI=Af%Ey^aUhZ)G%c zj>B~rh2dZ)kb5H>pTY^SyQ3`UVVyvFvFSf=Xzj6WklJIrAhbstsXev}ckR(eXpc7k zF`ix?v;OI&hu7Y){$4YlUiGp5>7|F)-mv~&GoD`cvHt0$hu7Y){$4ZgZGE6`-x$~l zc6XEwxIrf%8;xF;J(0w+z##S6yK`KGvV+M|CUce^m^;19l2oV0VQqe{k&U zV_+ND-BFg?4{}soA?u&t%L8|;zu!fa@SpXUAWwm!tiJ*xN`%1rOOU5PQPy7p5hX%k z{Uyj#peXCFfQS+yfc{s;KoIN{0$_KAglk4oICen{P`(A>+P@D4S^xZA9=K)w{Vt+} z|E#|Rc?uL|{S^>VA_Uf7f;~s*Uf2XNaq9E(9gp8dIg7xn-bxIUu{gsfh(?PKQou*ESf~>z1GIlx$Y5mIs z0i8gn9g7r%V;7L1a})6Xzw5O>+GPFndwJI1?+VKK&-%;p=l9aAzuy&<^PlyX zaMT#w}TWPp%6 zpG#tK)(bj;kKP~oDuw@iHbJNG_TfUE!hwy~y-Wde6DmoR=U$)_Aj#VO#uCag!1~Ma=l9aAzuy&<^PlyXy?nr>B%U1jicVok_Ik)Mp-=^OcP!3&4zA)?bc4 zzn5nH{jOkY{nwXfT}LB#n7-5pUbC~0oFgY{HMBh)<4w> zq_%$6Keha)x^~t-)e5Axe%3#={HMBh)}K~DPm34&C_3Wtc9^2510HW1!3zYWXxmaC zQa^7;S_Ja}O~D``Oo)PfKuFOKps~T*;^*I>#5_SJn;&dj@G=i?w6$)vjS)wq$2HHk zuK}Wq01E&a+FN20cOAx24J4#g*Gh9t%JGY1?1H0%WRkj+ZH@=vI{G* z#g1IqVn-fqu_G5&?6}^Au6c53j=y`ge`w!jcH|s~b}V8zYzxS}5%w2x0=A+mSiFLfLwu5hC?6@w*j_X$JxE{+N*oWh{a}d=Y+X3s} zPvjt0d+KUCwAU*g2*S3A0Bl=`zI-dVIem|Z61nsdMu>LdywziL-1HZNZ@<0$`h~h6<0bKwcJ)}o0$u6*g<9Gi4 zP4Dxcs6DO=+T%LwPcvX^`}kS#Tl-&<9fTO7SezZu1<=vM=f8AdfelaP1Ys`uD1`66 zwEX=m#*XVk?6}VH(+t?O$F?A9kL#>|dY}InU0My#PP1@y?bQQCynSfGP!YlXHw8qT zd~ry=2xt%0Hl(}$LhQ(c7(1?uvE#ZJJFbhdU?gi^Es`~=dyNq4y)t2t6hJFqig8!oGXss z_FQMy-~JqH)!M)F)8U-g-T53R=X0B!&uMZ#m&y4YCTD%=w$1U|pX+hV$z2m&?_IcW!^RheUVNbWuyVIWz^ICHJ_UAaT{?2DsDAoR*p9kl5$9Y@q^E5j5 zS8bc)ch1|&`ipawdfndb(-WL`%bc^oW!b1{nOM$ksbw}Ac4BEvk^pvpEEh zr5msDzORSr2Z0P397~)hVgz71z?vuG`>??;W*`k2fd0?ud;+$y^tN&I))@M93xWOj zUJ=Nl5yqt3#$yX(v2Ek9qhqjz@z>sa9+=5cTxLc|Kk+Mm49J8kK3Va zV_3xebr18_*#F2Fx^&=Q91|jp2XSpb;us+I-;E~neOvqYJOARC3Sm5j8v(X41mgIC zRGoj=|M2*oRPit5O(tFIvb67V}}nTu?C&TFimAI6_qU@;FlBwRz8lE zOJwB{8978!2alt0s_KX9^GHsNl@Alig|YHr!Z|RJ{FgXFNobc;)erTWc^%=Lh)6z% z(|Ie0Ba*-2w0Cwp4eB@Z63m3AcXIKNBSU9&&h?29)0q0K&M ztN%ID_v!qcB)V<&E2pS_(_gbs((F$)`%=w*RI?A&>_0WjC2N`T`pt3LW`AI`FEHE> zX!Zd{`u~y@w|N|^PcKFFn|_$fXkSpcAIp5-t7Wq4*UcA>_CjE{4C2CfBM8DW0bp0p z`CuN`T(%YJIsp4Auv=mCg zg8lKj(sY7Mjl|e{`kt{Z$e>?VBCS!#`hw8C*t=pei!5SFn$M{ z_Z{8JiSOfg3VxsAcNu<<;dfXf_nnM$GM + + + + + + + + + + + + + refine base headless example + + + + + +
+ + + + \ No newline at end of file diff --git a/examples/infinity-list/public/manifest.json b/examples/infinity-list/public/manifest.json new file mode 100644 index 000000000000..bb2fa367eba4 --- /dev/null +++ b/examples/infinity-list/public/manifest.json @@ -0,0 +1,15 @@ +{ + "short_name": "refine base infinity list example", + "name": "refine base infinity list example", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} \ No newline at end of file diff --git a/examples/infinity-list/src/App.css b/examples/infinity-list/src/App.css new file mode 100644 index 000000000000..20684948f1a9 --- /dev/null +++ b/examples/infinity-list/src/App.css @@ -0,0 +1,29 @@ +table { + border-spacing: 0; + border: 1px solid black; +} + +table th, +td { + margin: 0; + padding: 0.5rem; + border-bottom: 1px solid black; + border-right: 1px solid black; +} + +table tr:last-child td { + border-bottom: 0; +} + +table th, +td { + margin: 0; + padding: 0.5rem; + border-bottom: 1px solid black; + border-right: 1px solid black; +} + +table th:last-child, +td:last-child { + border-right: 0; +} diff --git a/examples/infinity-list/src/App.tsx b/examples/infinity-list/src/App.tsx new file mode 100644 index 000000000000..4593610b5d17 --- /dev/null +++ b/examples/infinity-list/src/App.tsx @@ -0,0 +1,23 @@ +import { Refine } from "@pankod/refine-core"; +import routerProvider from "@pankod/refine-react-router-v6"; +import dataProvider from "@pankod/refine-simple-rest"; + +import { PostList } from "pages/posts/list"; +import "./App.css"; + +const App: React.FC = () => { + return ( + + ); +}; + +export default App; diff --git a/examples/infinity-list/src/index.tsx b/examples/infinity-list/src/index.tsx new file mode 100644 index 000000000000..ed9fcf1e6b0b --- /dev/null +++ b/examples/infinity-list/src/index.tsx @@ -0,0 +1,13 @@ +import React from "react"; +import { createRoot } from "react-dom/client"; + +import App from "./App"; + +const container = document.getElementById("root"); +// eslint-disable-next-line +const root = createRoot(container!); +root.render( + + + , +); diff --git a/examples/infinity-list/src/interfaces/index.d.ts b/examples/infinity-list/src/interfaces/index.d.ts new file mode 100644 index 000000000000..4513bec0b48d --- /dev/null +++ b/examples/infinity-list/src/interfaces/index.d.ts @@ -0,0 +1,6 @@ +export interface IProduct { + id: number; + name: string; + material: string; + description: string; +} diff --git a/examples/infinity-list/src/pages/posts/list.tsx b/examples/infinity-list/src/pages/posts/list.tsx new file mode 100644 index 000000000000..2b159e511f7f --- /dev/null +++ b/examples/infinity-list/src/pages/posts/list.tsx @@ -0,0 +1,47 @@ +import React from "react"; +import { useInfinityGetList } from "@pankod/refine-core"; + +import { IProduct } from "interfaces"; + +export const PostList: React.FC = () => { + const { + data, + error, + hasNextPage, + isLoading, + fetchNextPage, + isFetchingNextPage, + } = useInfinityGetList({ + resource: "products", + }); + + if (isLoading) { + return

Loading

; + } + if (error) { + return

ERROR

; + } + + return ( +
+ {data?.pages.map((page) => + page.data.map((product) => ( +
  • + {product.id}-{product.name} +
  • + )), + )} + + +
    + ); +}; diff --git a/examples/infinity-list/src/react-app-env.d.ts b/examples/infinity-list/src/react-app-env.d.ts new file mode 100644 index 000000000000..6431bc5fc6b2 --- /dev/null +++ b/examples/infinity-list/src/react-app-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/infinity-list/tsconfig.json b/examples/infinity-list/tsconfig.json new file mode 100644 index 000000000000..5fecd495dbcf --- /dev/null +++ b/examples/infinity-list/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "baseUrl": "src", + "target": "es5", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx" + }, + "include": [ + "src", + ] +} \ No newline at end of file From 8e376316ab0f2facd80dfde86ef0e273c12f00d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C4=B1ld=C4=B1ray=20=C3=9Cnl=C3=BC?= Date: Fri, 13 Jan 2023 15:51:58 +0300 Subject: [PATCH 03/29] feat: add useInfinityList on subscriptionType for useResourceSubscription hook --- packages/core/src/hooks/live/useResourceSubscription/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/hooks/live/useResourceSubscription/index.ts b/packages/core/src/hooks/live/useResourceSubscription/index.ts index bcc162eab756..47ea80cf0bfb 100644 --- a/packages/core/src/hooks/live/useResourceSubscription/index.ts +++ b/packages/core/src/hooks/live/useResourceSubscription/index.ts @@ -25,7 +25,7 @@ export type UseResourceSubscriptionProps = { hasPagination?: boolean; sort?: CrudSorting; filters?: CrudFilters; - subscriptionType: "useList" | "useOne" | "useMany"; + subscriptionType: "useList" | "useOne" | "useMany" | "useInfinityList"; [key: string]: any; }; types: LiveEvent["type"][]; From f2429923294511bd044f6aa842027970efa0058d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C4=B1ld=C4=B1ray=20=C3=9Cnl=C3=BC?= Date: Fri, 13 Jan 2023 15:52:30 +0300 Subject: [PATCH 04/29] feat: update query options on useInfinityGetList hook --- .../core/src/contexts/data/IDataContext.ts | 5 + .../core/src/hooks/data/useInfinityGetList.ts | 91 ++++++++++++++++--- 2 files changed, 84 insertions(+), 12 deletions(-) diff --git a/packages/core/src/contexts/data/IDataContext.ts b/packages/core/src/contexts/data/IDataContext.ts index 48d93e70ff10..5132a0fb789d 100644 --- a/packages/core/src/contexts/data/IDataContext.ts +++ b/packages/core/src/contexts/data/IDataContext.ts @@ -89,6 +89,11 @@ export interface GetListResponse { total: number; } +export interface GetInfinityListResponse + extends GetListResponse { + pagination?: Pagination; +} + export interface CreateResponse { data: TData; } diff --git a/packages/core/src/hooks/data/useInfinityGetList.ts b/packages/core/src/hooks/data/useInfinityGetList.ts index 6443d8d2ebfb..e31c75f2db37 100644 --- a/packages/core/src/hooks/data/useInfinityGetList.ts +++ b/packages/core/src/hooks/data/useInfinityGetList.ts @@ -1,12 +1,9 @@ import { - QueryObserverResult, - UseQueryOptions, useInfiniteQuery, UseInfiniteQueryOptions, InfiniteQueryObserverResult, } from "@tanstack/react-query"; import { - GetListResponse, CrudFilters, Pagination, BaseRecord, @@ -15,6 +12,7 @@ import { MetaDataQuery, SuccessErrorNotification, LiveModeProps, + GetInfinityListResponse, } from "../../interfaces"; import { useResource, @@ -26,7 +24,7 @@ import { } from "@hooks"; import { queryKeys, pickDataProvider } from "@definitions/helpers"; -export interface UseListConfig { +export interface UseInfinityConfig { pagination?: Pagination; hasPagination?: boolean; sort?: CrudSorting; @@ -42,11 +40,14 @@ export type UseInfinityListProps = { * Configuration for pagination, sorting and filtering * @type [`UseListConfig`](/docs/api-reference/core/hooks/data/useList/#config-parameters) */ - config?: UseListConfig; + config?: UseInfinityConfig; /** - * react-query's [useQuery](https://tanstack.com/query/v4/docs/react/reference/useInfiniteQuery) options, + * react-query's [useInfiniteQuery](https://tanstack.com/query/v4/docs/react/reference/useInfiniteQuery) options, */ - queryOptions?: UseInfiniteQueryOptions, TError>; + queryOptions?: UseInfiniteQueryOptions< + GetInfinityListResponse, + TError + >; /** * Metadata query for `dataProvider` */ @@ -59,11 +60,11 @@ export type UseInfinityListProps = { LiveModeProps; /** - * `useList` is a modified version of `react-query`'s {@link https://react-query.tanstack.com/guides/queries `useQuery`} used for retrieving items from a `resource` with pagination, sort, and filter configurations. + * `useInfinityGetList` is a modified version of `react-query`'s {@link https://tanstack.com/query/latest/docs/react/guides/infinite-queries `useInfiniteQuery`} used for retrieving items from a `resource` with pagination, sort, and filter configurations. * * It uses the `getList` method as the query function from the `dataProvider` which is passed to ``. * - * @see {@link https://refine.dev/docs/core/hooks/data/useList} for more details. + * @see {@link https://refine.dev/docs/core/hooks/data/useInfinityGetList} for more details. * * @typeParam TData - Result data of the query extends {@link https://refine.dev/docs/core/interfaceReferences#baserecord `BaseRecord`} * @typeParam TError - Custom error object that extends {@link https://refine.dev/docs/core/interfaceReferences#httperror `HttpError`} @@ -84,7 +85,7 @@ export const useInfinityGetList = < liveParams, dataProviderName, }: UseInfinityListProps): InfiniteQueryObserverResult< - GetListResponse, + GetInfinityListResponse, TError > => { const { resources } = useResource(); @@ -114,7 +115,7 @@ export const useInfinityGetList = < hasPagination: config?.hasPagination, sort: config?.sort, filters: config?.filters, - subscriptionType: "useList", + subscriptionType: "useInfinityList", ...liveParams, }, channel: `resources/${resource}`, @@ -123,13 +124,22 @@ export const useInfinityGetList = < onLiveEvent, }); - const queryResponse = useInfiniteQuery, TError>( + const queryResponse = useInfiniteQuery< + GetInfinityListResponse, + TError + >( queryKey.list(config), ({ queryKey, pageParam, signal }) => { const { hasPagination, ...restConfig } = config || {}; + const pagination = { + ...config?.pagination, + current: pageParam, + }; + return getList({ resource, ...restConfig, + pagination, hasPagination, metaData: { ...metaData, @@ -139,8 +149,65 @@ export const useInfinityGetList = < signal, }, }, + }).then(({ data, total }) => { + return { + data, + total, + pagination, + }; }); }, + { + ...queryOptions, + onSuccess: (data) => { + queryOptions?.onSuccess?.(data); + + const notificationConfig = + typeof successNotification === "function" + ? successNotification( + data, + { metaData, config }, + resource, + ) + : successNotification; + + handleNotification(notificationConfig); + }, + onError: (err: TError) => { + checkError(err); + queryOptions?.onError?.(err); + + const notificationConfig = + typeof errorNotification === "function" + ? errorNotification(err, { metaData, config }, resource) + : errorNotification; + + handleNotification(notificationConfig, { + key: `${resource}-useList-notification`, + message: translate( + "common:notifications.error", + { statusCode: err.statusCode }, + `Error (status code: ${err.statusCode})`, + ), + description: err.message, + type: "error", + }); + }, + getNextPageParam: (lastPage) => { + const { pagination } = lastPage; + const current = pagination?.current || 1; + const pageSize = pagination?.pageSize || 10; + + const totalPages = Math.ceil((lastPage.total || 0) / pageSize); + return current < totalPages ? Number(current) + 1 : undefined; + }, + getPreviousPageParam: (lastPage) => { + const { pagination } = lastPage; + const current = pagination?.current || 1; + + return current === 1 ? undefined : current - 1; + }, + }, ); return queryResponse; From 1b8ecab49c22ee03e9fbc8234ccef57d29edb33e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C4=B1ld=C4=B1ray=20=C3=9Cnl=C3=BC?= Date: Fri, 13 Jan 2023 16:32:13 +0300 Subject: [PATCH 05/29] fix: useResourceSubscription on subscriptionType --- packages/core/src/hooks/data/useInfinityGetList.ts | 2 +- packages/core/src/hooks/live/useResourceSubscription/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/hooks/data/useInfinityGetList.ts b/packages/core/src/hooks/data/useInfinityGetList.ts index e31c75f2db37..77367ba7206e 100644 --- a/packages/core/src/hooks/data/useInfinityGetList.ts +++ b/packages/core/src/hooks/data/useInfinityGetList.ts @@ -115,7 +115,7 @@ export const useInfinityGetList = < hasPagination: config?.hasPagination, sort: config?.sort, filters: config?.filters, - subscriptionType: "useInfinityList", + subscriptionType: "useList", ...liveParams, }, channel: `resources/${resource}`, diff --git a/packages/core/src/hooks/live/useResourceSubscription/index.ts b/packages/core/src/hooks/live/useResourceSubscription/index.ts index 47ea80cf0bfb..bcc162eab756 100644 --- a/packages/core/src/hooks/live/useResourceSubscription/index.ts +++ b/packages/core/src/hooks/live/useResourceSubscription/index.ts @@ -25,7 +25,7 @@ export type UseResourceSubscriptionProps = { hasPagination?: boolean; sort?: CrudSorting; filters?: CrudFilters; - subscriptionType: "useList" | "useOne" | "useMany" | "useInfinityList"; + subscriptionType: "useList" | "useOne" | "useMany"; [key: string]: any; }; types: LiveEvent["type"][]; From c46179cbc2126e7d3e4765709d492787fb59bdf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C4=B1ld=C4=B1ray=20=C3=9Cnl=C3=BC?= Date: Fri, 13 Jan 2023 16:51:27 +0300 Subject: [PATCH 06/29] test: add useInfinityGetList hook --- .../hooks/data/useInfinityGetList.spec.tsx | 207 ++++++++++++++++++ 1 file changed, 207 insertions(+) create mode 100644 packages/core/src/hooks/data/useInfinityGetList.spec.tsx diff --git a/packages/core/src/hooks/data/useInfinityGetList.spec.tsx b/packages/core/src/hooks/data/useInfinityGetList.spec.tsx new file mode 100644 index 000000000000..7d89d387eb5d --- /dev/null +++ b/packages/core/src/hooks/data/useInfinityGetList.spec.tsx @@ -0,0 +1,207 @@ +import { renderHook, waitFor } from "@testing-library/react"; + +import { MockJSONServer, TestWrapper } from "@test"; + +import { useInfinityGetList } from "./useInfinityGetList"; +import { defaultRefineOptions } from "@contexts/refine"; +import { IRefineContextProvider } from "../../interfaces"; + +const mockRefineProvider: IRefineContextProvider = { + hasDashboard: false, + ...defaultRefineOptions, + options: defaultRefineOptions, +}; + +describe("useInfinityGetList Hook", () => { + it("with rest json server", async () => { + const { result } = renderHook( + () => useInfinityGetList({ resource: "posts" }), + { + wrapper: TestWrapper({ + dataProvider: MockJSONServer, + resources: [{ name: "posts" }], + }), + }, + ); + + await waitFor(() => { + expect(result.current.isSuccess).toBeTruthy(); + }); + + const { data } = result.current; + + expect(data?.pages).toHaveLength(1); + expect(data?.pages[0].data).toHaveLength(2); + expect(data?.pages[0].total).toEqual(2); + }); + + it("hasNextPage is truthy", async () => { + const { result } = renderHook( + () => + useInfinityGetList({ + resource: "posts", + config: { + pagination: { + pageSize: 1, + }, + }, + }), + { + wrapper: TestWrapper({ + dataProvider: MockJSONServer, + resources: [{ name: "posts" }], + }), + }, + ); + + await waitFor(() => { + expect(result.current.isSuccess).toBeTruthy(); + }); + + const { hasNextPage } = result.current; + expect(hasNextPage).toBeTruthy(); + }); + + describe("useResourceSubscription", () => { + it("useSubscription", async () => { + const onSubscribeMock = jest.fn(); + + const { result } = renderHook( + () => + useInfinityGetList({ + resource: "posts", + }), + { + wrapper: TestWrapper({ + dataProvider: MockJSONServer, + resources: [{ name: "posts" }], + liveProvider: { + unsubscribe: jest.fn(), + subscribe: onSubscribeMock, + }, + refineProvider: { + ...mockRefineProvider, + liveMode: "auto", + }, + }), + }, + ); + + await waitFor(() => { + expect(result.current.isSuccess).toBeTruthy(); + }); + + expect(onSubscribeMock).toBeCalled(); + expect(onSubscribeMock).toHaveBeenCalledWith({ + channel: "resources/posts", + callback: expect.any(Function), + params: { + filters: undefined, + hasPagination: undefined, + metaData: undefined, + pagination: undefined, + resource: "posts", + sort: undefined, + subscriptionType: "useList", + }, + types: ["*"], + }); + }); + + it("liveMode = Off useSubscription", async () => { + const onSubscribeMock = jest.fn(); + + const { result } = renderHook( + () => + useInfinityGetList({ + resource: "posts", + }), + { + wrapper: TestWrapper({ + dataProvider: MockJSONServer, + resources: [{ name: "posts" }], + liveProvider: { + unsubscribe: jest.fn(), + subscribe: onSubscribeMock, + }, + refineProvider: { + ...mockRefineProvider, + liveMode: "off", + }, + }), + }, + ); + + await waitFor(() => { + expect(result.current.isSuccess).toBeTruthy(); + }); + + expect(onSubscribeMock).not.toBeCalled(); + }); + + it("liveMode = Off and liveMode hook param auto", async () => { + const onSubscribeMock = jest.fn(); + + const { result } = renderHook( + () => + useInfinityGetList({ resource: "posts", liveMode: "auto" }), + { + wrapper: TestWrapper({ + dataProvider: MockJSONServer, + resources: [{ name: "posts" }], + liveProvider: { + unsubscribe: jest.fn(), + subscribe: onSubscribeMock, + }, + refineProvider: { + ...mockRefineProvider, + liveMode: "off", + }, + }), + }, + ); + + await waitFor(() => { + expect(result.current.isSuccess).toBeTruthy(); + }); + + expect(onSubscribeMock).toBeCalled(); + }); + + it("unsubscribe call on unmount", async () => { + const onSubscribeMock = jest.fn(() => true); + const onUnsubscribeMock = jest.fn(); + + const { result, unmount } = renderHook( + () => + useInfinityGetList({ + resource: "posts", + }), + { + wrapper: TestWrapper({ + dataProvider: MockJSONServer, + resources: [{ name: "posts" }], + liveProvider: { + unsubscribe: onUnsubscribeMock, + subscribe: onSubscribeMock, + }, + refineProvider: { + ...mockRefineProvider, + liveMode: "auto", + }, + }), + }, + ); + + await waitFor(() => { + expect(result.current.isSuccess).toBeTruthy(); + }); + + expect(onSubscribeMock).toBeCalled(); + + unmount(); + expect(onUnsubscribeMock).toBeCalledWith(true); + expect(onUnsubscribeMock).toBeCalledTimes(1); + }); + }); +}); From 184b23b3ee258fd5db5b61b482f6b1b8583b3bb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C4=B1ld=C4=B1ray=20=C3=9Cnl=C3=BC?= Date: Sat, 14 Jan 2023 22:41:44 +0300 Subject: [PATCH 07/29] chore: fix method docs --- packages/core/src/hooks/data/useInfinityGetList.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/hooks/data/useInfinityGetList.ts b/packages/core/src/hooks/data/useInfinityGetList.ts index 77367ba7206e..f2311042b21e 100644 --- a/packages/core/src/hooks/data/useInfinityGetList.ts +++ b/packages/core/src/hooks/data/useInfinityGetList.ts @@ -38,7 +38,7 @@ export type UseInfinityListProps = { resource: string; /** * Configuration for pagination, sorting and filtering - * @type [`UseListConfig`](/docs/api-reference/core/hooks/data/useList/#config-parameters) + * @type [`UseInfinityConfig`](/docs/api-reference/core/hooks/data/useInfinityGetList/#config-parameters) */ config?: UseInfinityConfig; /** @@ -183,7 +183,7 @@ export const useInfinityGetList = < : errorNotification; handleNotification(notificationConfig, { - key: `${resource}-useList-notification`, + key: `${resource}-useInfinityGetList-notification`, message: translate( "common:notifications.error", { statusCode: err.statusCode }, From c814208892ad7b4b51ad131a45b47e0658b7bee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C4=B1ld=C4=B1ray=20=C3=9Cnl=C3=BC?= Date: Mon, 16 Jan 2023 09:39:50 +0300 Subject: [PATCH 08/29] refactor: rename useInfinityGetList o useInfinityList --- packages/core/src/hooks/data/index.ts | 2 +- ...etList.spec.tsx => useInfinityList.spec.tsx} | 17 ++++++++--------- ...useInfinityGetList.ts => useInfinityList.ts} | 10 +++++----- 3 files changed, 14 insertions(+), 15 deletions(-) rename packages/core/src/hooks/data/{useInfinityGetList.spec.tsx => useInfinityList.spec.tsx} (93%) rename packages/core/src/hooks/data/{useInfinityGetList.ts => useInfinityList.ts} (94%) diff --git a/packages/core/src/hooks/data/index.ts b/packages/core/src/hooks/data/index.ts index 9da2d40dd53f..a1083462d73f 100644 --- a/packages/core/src/hooks/data/index.ts +++ b/packages/core/src/hooks/data/index.ts @@ -15,4 +15,4 @@ export { useCustom } from "./useCustom"; export { useCustomMutation } from "./useCustomMutation"; export { useDataProvider } from "./useDataProvider"; -export { useInfinityGetList } from "./useInfinityGetList"; +export { useInfinityGetList } from "./useInfinityList"; diff --git a/packages/core/src/hooks/data/useInfinityGetList.spec.tsx b/packages/core/src/hooks/data/useInfinityList.spec.tsx similarity index 93% rename from packages/core/src/hooks/data/useInfinityGetList.spec.tsx rename to packages/core/src/hooks/data/useInfinityList.spec.tsx index 7d89d387eb5d..4219859d1c8e 100644 --- a/packages/core/src/hooks/data/useInfinityGetList.spec.tsx +++ b/packages/core/src/hooks/data/useInfinityList.spec.tsx @@ -2,7 +2,7 @@ import { renderHook, waitFor } from "@testing-library/react"; import { MockJSONServer, TestWrapper } from "@test"; -import { useInfinityGetList } from "./useInfinityGetList"; +import { useInfinityList } from "./useInfinityList"; import { defaultRefineOptions } from "@contexts/refine"; import { IRefineContextProvider } from "../../interfaces"; @@ -12,10 +12,10 @@ const mockRefineProvider: IRefineContextProvider = { options: defaultRefineOptions, }; -describe("useInfinityGetList Hook", () => { +describe("useInfinityList Hook", () => { it("with rest json server", async () => { const { result } = renderHook( - () => useInfinityGetList({ resource: "posts" }), + () => useInfinityList({ resource: "posts" }), { wrapper: TestWrapper({ dataProvider: MockJSONServer, @@ -38,7 +38,7 @@ describe("useInfinityGetList Hook", () => { it("hasNextPage is truthy", async () => { const { result } = renderHook( () => - useInfinityGetList({ + useInfinityList({ resource: "posts", config: { pagination: { @@ -68,7 +68,7 @@ describe("useInfinityGetList Hook", () => { const { result } = renderHook( () => - useInfinityGetList({ + useInfinityList({ resource: "posts", }), { @@ -113,7 +113,7 @@ describe("useInfinityGetList Hook", () => { const { result } = renderHook( () => - useInfinityGetList({ + useInfinityList({ resource: "posts", }), { @@ -143,8 +143,7 @@ describe("useInfinityGetList Hook", () => { const onSubscribeMock = jest.fn(); const { result } = renderHook( - () => - useInfinityGetList({ resource: "posts", liveMode: "auto" }), + () => useInfinityList({ resource: "posts", liveMode: "auto" }), { wrapper: TestWrapper({ dataProvider: MockJSONServer, @@ -174,7 +173,7 @@ describe("useInfinityGetList Hook", () => { const { result, unmount } = renderHook( () => - useInfinityGetList({ + useInfinityList({ resource: "posts", }), { diff --git a/packages/core/src/hooks/data/useInfinityGetList.ts b/packages/core/src/hooks/data/useInfinityList.ts similarity index 94% rename from packages/core/src/hooks/data/useInfinityGetList.ts rename to packages/core/src/hooks/data/useInfinityList.ts index f2311042b21e..fd40639f4300 100644 --- a/packages/core/src/hooks/data/useInfinityGetList.ts +++ b/packages/core/src/hooks/data/useInfinityList.ts @@ -38,7 +38,7 @@ export type UseInfinityListProps = { resource: string; /** * Configuration for pagination, sorting and filtering - * @type [`UseInfinityConfig`](/docs/api-reference/core/hooks/data/useInfinityGetList/#config-parameters) + * @type [`UseInfinityConfig`](/docs/api-reference/core/hooks/data/useInfinityList/#config-parameters) */ config?: UseInfinityConfig; /** @@ -60,17 +60,17 @@ export type UseInfinityListProps = { LiveModeProps; /** - * `useInfinityGetList` is a modified version of `react-query`'s {@link https://tanstack.com/query/latest/docs/react/guides/infinite-queries `useInfiniteQuery`} used for retrieving items from a `resource` with pagination, sort, and filter configurations. + * `useInfinityList` is a modified version of `react-query`'s {@link https://tanstack.com/query/latest/docs/react/guides/infinite-queries `useInfiniteQuery`} used for retrieving items from a `resource` with pagination, sort, and filter configurations. * * It uses the `getList` method as the query function from the `dataProvider` which is passed to ``. * - * @see {@link https://refine.dev/docs/core/hooks/data/useInfinityGetList} for more details. + * @see {@link https://refine.dev/docs/core/hooks/data/useInfinityList} for more details. * * @typeParam TData - Result data of the query extends {@link https://refine.dev/docs/core/interfaceReferences#baserecord `BaseRecord`} * @typeParam TError - Custom error object that extends {@link https://refine.dev/docs/core/interfaceReferences#httperror `HttpError`} * */ -export const useInfinityGetList = < +export const useInfinityList = < TData extends BaseRecord = BaseRecord, TError extends HttpError = HttpError, >({ @@ -183,7 +183,7 @@ export const useInfinityGetList = < : errorNotification; handleNotification(notificationConfig, { - key: `${resource}-useInfinityGetList-notification`, + key: `${resource}-useInfinityList-notification`, message: translate( "common:notifications.error", { statusCode: err.statusCode }, From 756a37ec39602b48b1e4a04e6eb16999cf16a6bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C4=B1ld=C4=B1ray=20=C3=9Cnl=C3=BC?= Date: Mon, 16 Jan 2023 09:44:23 +0300 Subject: [PATCH 09/29] fix: infinity list example name --- examples/infinity-list/README.md | 4 ++-- examples/infinity-list/public/index.html | 2 +- examples/infinity-list/public/manifest.json | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/infinity-list/README.md b/examples/infinity-list/README.md index 573fba82a078..a5fae64a2d5d 100644 --- a/examples/infinity-list/README.md +++ b/examples/infinity-list/README.md @@ -34,12 +34,12 @@ ## Try it out on your local ```bash -npm create refine-app@latest -- --example base-headless +npm create refine-app@latest -- --example infinity-list ``` ## Try it out on StackBlitz
    -[![Open base-headless example from refine](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/refinedev/refine/tree/master/examples/base-headless?terminal=start&preset=node) +[![Open infinity list example from refine](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/refinedev/refine/tree/master/examples/infinity-list?terminal=start&preset=node) diff --git a/examples/infinity-list/public/index.html b/examples/infinity-list/public/index.html index fe8748b2711f..13c3983c50da 100644 --- a/examples/infinity-list/public/index.html +++ b/examples/infinity-list/public/index.html @@ -22,7 +22,7 @@ work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> - refine base headless example + refine infinity list example diff --git a/examples/infinity-list/public/manifest.json b/examples/infinity-list/public/manifest.json index bb2fa367eba4..726bc8cdfab4 100644 --- a/examples/infinity-list/public/manifest.json +++ b/examples/infinity-list/public/manifest.json @@ -1,6 +1,6 @@ { - "short_name": "refine base infinity list example", - "name": "refine base infinity list example", + "short_name": "refine infinity list example", + "name": "refine infinity list example", "icons": [ { "src": "favicon.ico", From bc3c8a855e5fb1feac470db594ce26e702589a76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C4=B1ld=C4=B1ray=20=C3=9Cnl=C3=BC?= Date: Mon, 16 Jan 2023 10:47:43 +0300 Subject: [PATCH 10/29] refactor: rename useInfinityList to useInfiniteQuery --- examples/infinity-list/src/pages/posts/list.tsx | 4 ++-- packages/core/src/hooks/data/index.ts | 2 +- ...ityList.spec.tsx => useInfiniteList.spec.tsx} | 16 ++++++++-------- .../{useInfinityList.ts => useInfiniteList.ts} | 10 +++++----- 4 files changed, 16 insertions(+), 16 deletions(-) rename packages/core/src/hooks/data/{useInfinityList.spec.tsx => useInfiniteList.spec.tsx} (94%) rename packages/core/src/hooks/data/{useInfinityList.ts => useInfiniteList.ts} (96%) diff --git a/examples/infinity-list/src/pages/posts/list.tsx b/examples/infinity-list/src/pages/posts/list.tsx index 2b159e511f7f..213cbf693b01 100644 --- a/examples/infinity-list/src/pages/posts/list.tsx +++ b/examples/infinity-list/src/pages/posts/list.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { useInfinityGetList } from "@pankod/refine-core"; +import { useInfiniteList } from "@pankod/refine-core"; import { IProduct } from "interfaces"; @@ -11,7 +11,7 @@ export const PostList: React.FC = () => { isLoading, fetchNextPage, isFetchingNextPage, - } = useInfinityGetList({ + } = useInfiniteList({ resource: "products", }); diff --git a/packages/core/src/hooks/data/index.ts b/packages/core/src/hooks/data/index.ts index a1083462d73f..94be2377487f 100644 --- a/packages/core/src/hooks/data/index.ts +++ b/packages/core/src/hooks/data/index.ts @@ -15,4 +15,4 @@ export { useCustom } from "./useCustom"; export { useCustomMutation } from "./useCustomMutation"; export { useDataProvider } from "./useDataProvider"; -export { useInfinityGetList } from "./useInfinityList"; +export { useInfiniteList } from "./useInfiniteList"; diff --git a/packages/core/src/hooks/data/useInfinityList.spec.tsx b/packages/core/src/hooks/data/useInfiniteList.spec.tsx similarity index 94% rename from packages/core/src/hooks/data/useInfinityList.spec.tsx rename to packages/core/src/hooks/data/useInfiniteList.spec.tsx index 4219859d1c8e..09b381f1d535 100644 --- a/packages/core/src/hooks/data/useInfinityList.spec.tsx +++ b/packages/core/src/hooks/data/useInfiniteList.spec.tsx @@ -2,7 +2,7 @@ import { renderHook, waitFor } from "@testing-library/react"; import { MockJSONServer, TestWrapper } from "@test"; -import { useInfinityList } from "./useInfinityList"; +import { useInfiniteList } from "./useInfiniteList"; import { defaultRefineOptions } from "@contexts/refine"; import { IRefineContextProvider } from "../../interfaces"; @@ -12,10 +12,10 @@ const mockRefineProvider: IRefineContextProvider = { options: defaultRefineOptions, }; -describe("useInfinityList Hook", () => { +describe("useInfiniteList Hook", () => { it("with rest json server", async () => { const { result } = renderHook( - () => useInfinityList({ resource: "posts" }), + () => useInfiniteList({ resource: "posts" }), { wrapper: TestWrapper({ dataProvider: MockJSONServer, @@ -38,7 +38,7 @@ describe("useInfinityList Hook", () => { it("hasNextPage is truthy", async () => { const { result } = renderHook( () => - useInfinityList({ + useInfiniteList({ resource: "posts", config: { pagination: { @@ -68,7 +68,7 @@ describe("useInfinityList Hook", () => { const { result } = renderHook( () => - useInfinityList({ + useInfiniteList({ resource: "posts", }), { @@ -113,7 +113,7 @@ describe("useInfinityList Hook", () => { const { result } = renderHook( () => - useInfinityList({ + useInfiniteList({ resource: "posts", }), { @@ -143,7 +143,7 @@ describe("useInfinityList Hook", () => { const onSubscribeMock = jest.fn(); const { result } = renderHook( - () => useInfinityList({ resource: "posts", liveMode: "auto" }), + () => useInfiniteList({ resource: "posts", liveMode: "auto" }), { wrapper: TestWrapper({ dataProvider: MockJSONServer, @@ -173,7 +173,7 @@ describe("useInfinityList Hook", () => { const { result, unmount } = renderHook( () => - useInfinityList({ + useInfiniteList({ resource: "posts", }), { diff --git a/packages/core/src/hooks/data/useInfinityList.ts b/packages/core/src/hooks/data/useInfiniteList.ts similarity index 96% rename from packages/core/src/hooks/data/useInfinityList.ts rename to packages/core/src/hooks/data/useInfiniteList.ts index fd40639f4300..77b881b92a91 100644 --- a/packages/core/src/hooks/data/useInfinityList.ts +++ b/packages/core/src/hooks/data/useInfiniteList.ts @@ -38,7 +38,7 @@ export type UseInfinityListProps = { resource: string; /** * Configuration for pagination, sorting and filtering - * @type [`UseInfinityConfig`](/docs/api-reference/core/hooks/data/useInfinityList/#config-parameters) + * @type [`UseInfinityConfig`](/docs/api-reference/core/hooks/data/useInfiniteList/#config-parameters) */ config?: UseInfinityConfig; /** @@ -60,17 +60,17 @@ export type UseInfinityListProps = { LiveModeProps; /** - * `useInfinityList` is a modified version of `react-query`'s {@link https://tanstack.com/query/latest/docs/react/guides/infinite-queries `useInfiniteQuery`} used for retrieving items from a `resource` with pagination, sort, and filter configurations. + * `useInfiniteList` is a modified version of `react-query`'s {@link https://tanstack.com/query/latest/docs/react/guides/infinite-queries `useInfiniteQuery`} used for retrieving items from a `resource` with pagination, sort, and filter configurations. * * It uses the `getList` method as the query function from the `dataProvider` which is passed to ``. * - * @see {@link https://refine.dev/docs/core/hooks/data/useInfinityList} for more details. + * @see {@link https://refine.dev/docs/core/hooks/data/useInfiniteList} for more details. * * @typeParam TData - Result data of the query extends {@link https://refine.dev/docs/core/interfaceReferences#baserecord `BaseRecord`} * @typeParam TError - Custom error object that extends {@link https://refine.dev/docs/core/interfaceReferences#httperror `HttpError`} * */ -export const useInfinityList = < +export const useInfiniteList = < TData extends BaseRecord = BaseRecord, TError extends HttpError = HttpError, >({ @@ -183,7 +183,7 @@ export const useInfinityList = < : errorNotification; handleNotification(notificationConfig, { - key: `${resource}-useInfinityList-notification`, + key: `${resource}-useInfiniteList-notification`, message: translate( "common:notifications.error", { statusCode: err.statusCode }, From 8004504f02d137c0fffa21d4259f6ac5bd5a2518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C4=B1ld=C4=B1ray=20=C3=9Cnl=C3=BC?= Date: Mon, 16 Jan 2023 15:32:26 +0300 Subject: [PATCH 11/29] chore: rename example --- .../.gitignore | 0 .../README.md | 4 ++-- .../package.json | 2 +- .../public/favicon.ico | Bin .../public/index.html | 2 +- .../data-use-infinite-list/public/manifest.json | 15 +++++++++++++++ .../src/App.css | 0 .../src/App.tsx | 0 .../src/index.tsx | 0 .../src/interfaces/index.d.ts | 0 .../src/pages/posts/list.tsx | 0 .../src/react-app-env.d.ts | 0 .../tsconfig.json | 0 examples/infinity-list/public/manifest.json | 15 --------------- 14 files changed, 19 insertions(+), 19 deletions(-) rename examples/{infinity-list => data-use-infinite-list}/.gitignore (100%) rename examples/{infinity-list => data-use-infinite-list}/README.md (85%) rename examples/{infinity-list => data-use-infinite-list}/package.json (96%) rename examples/{infinity-list => data-use-infinite-list}/public/favicon.ico (100%) rename examples/{infinity-list => data-use-infinite-list}/public/index.html (96%) create mode 100644 examples/data-use-infinite-list/public/manifest.json rename examples/{infinity-list => data-use-infinite-list}/src/App.css (100%) rename examples/{infinity-list => data-use-infinite-list}/src/App.tsx (100%) rename examples/{infinity-list => data-use-infinite-list}/src/index.tsx (100%) rename examples/{infinity-list => data-use-infinite-list}/src/interfaces/index.d.ts (100%) rename examples/{infinity-list => data-use-infinite-list}/src/pages/posts/list.tsx (100%) rename examples/{infinity-list => data-use-infinite-list}/src/react-app-env.d.ts (100%) rename examples/{infinity-list => data-use-infinite-list}/tsconfig.json (100%) delete mode 100644 examples/infinity-list/public/manifest.json diff --git a/examples/infinity-list/.gitignore b/examples/data-use-infinite-list/.gitignore similarity index 100% rename from examples/infinity-list/.gitignore rename to examples/data-use-infinite-list/.gitignore diff --git a/examples/infinity-list/README.md b/examples/data-use-infinite-list/README.md similarity index 85% rename from examples/infinity-list/README.md rename to examples/data-use-infinite-list/README.md index a5fae64a2d5d..1957fdca5dde 100644 --- a/examples/infinity-list/README.md +++ b/examples/data-use-infinite-list/README.md @@ -34,12 +34,12 @@ ## Try it out on your local ```bash -npm create refine-app@latest -- --example infinity-list +npm create refine-app@latest -- --example data-use-infinite-list ``` ## Try it out on StackBlitz
    -[![Open infinity list example from refine](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/refinedev/refine/tree/master/examples/infinity-list?terminal=start&preset=node) +[![Open useInfiniteList hook example from refine](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/refinedev/refine/tree/master/examples/data-use-infinite-list?terminal=start&preset=node) diff --git a/examples/infinity-list/package.json b/examples/data-use-infinite-list/package.json similarity index 96% rename from examples/infinity-list/package.json rename to examples/data-use-infinite-list/package.json index 5f1182b67673..01f496599f02 100644 --- a/examples/infinity-list/package.json +++ b/examples/data-use-infinite-list/package.json @@ -1,5 +1,5 @@ { - "name": "infinity-list", + "name": "data-use-infinite-list", "version": "3.25.0", "private": true, "dependencies": { diff --git a/examples/infinity-list/public/favicon.ico b/examples/data-use-infinite-list/public/favicon.ico similarity index 100% rename from examples/infinity-list/public/favicon.ico rename to examples/data-use-infinite-list/public/favicon.ico diff --git a/examples/infinity-list/public/index.html b/examples/data-use-infinite-list/public/index.html similarity index 96% rename from examples/infinity-list/public/index.html rename to examples/data-use-infinite-list/public/index.html index 13c3983c50da..6b56c98b2eb8 100644 --- a/examples/infinity-list/public/index.html +++ b/examples/data-use-infinite-list/public/index.html @@ -22,7 +22,7 @@ work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> - refine infinity list example + refine useInfiniteList hook example diff --git a/examples/data-use-infinite-list/public/manifest.json b/examples/data-use-infinite-list/public/manifest.json new file mode 100644 index 000000000000..29a0b885989d --- /dev/null +++ b/examples/data-use-infinite-list/public/manifest.json @@ -0,0 +1,15 @@ +{ + "short_name": "refine useInfiniteList hook example", + "name": "refine useInfiniteList hook example", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/examples/infinity-list/src/App.css b/examples/data-use-infinite-list/src/App.css similarity index 100% rename from examples/infinity-list/src/App.css rename to examples/data-use-infinite-list/src/App.css diff --git a/examples/infinity-list/src/App.tsx b/examples/data-use-infinite-list/src/App.tsx similarity index 100% rename from examples/infinity-list/src/App.tsx rename to examples/data-use-infinite-list/src/App.tsx diff --git a/examples/infinity-list/src/index.tsx b/examples/data-use-infinite-list/src/index.tsx similarity index 100% rename from examples/infinity-list/src/index.tsx rename to examples/data-use-infinite-list/src/index.tsx diff --git a/examples/infinity-list/src/interfaces/index.d.ts b/examples/data-use-infinite-list/src/interfaces/index.d.ts similarity index 100% rename from examples/infinity-list/src/interfaces/index.d.ts rename to examples/data-use-infinite-list/src/interfaces/index.d.ts diff --git a/examples/infinity-list/src/pages/posts/list.tsx b/examples/data-use-infinite-list/src/pages/posts/list.tsx similarity index 100% rename from examples/infinity-list/src/pages/posts/list.tsx rename to examples/data-use-infinite-list/src/pages/posts/list.tsx diff --git a/examples/infinity-list/src/react-app-env.d.ts b/examples/data-use-infinite-list/src/react-app-env.d.ts similarity index 100% rename from examples/infinity-list/src/react-app-env.d.ts rename to examples/data-use-infinite-list/src/react-app-env.d.ts diff --git a/examples/infinity-list/tsconfig.json b/examples/data-use-infinite-list/tsconfig.json similarity index 100% rename from examples/infinity-list/tsconfig.json rename to examples/data-use-infinite-list/tsconfig.json diff --git a/examples/infinity-list/public/manifest.json b/examples/infinity-list/public/manifest.json deleted file mode 100644 index 726bc8cdfab4..000000000000 --- a/examples/infinity-list/public/manifest.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "short_name": "refine infinity list example", - "name": "refine infinity list example", - "icons": [ - { - "src": "favicon.ico", - "sizes": "64x64 32x32 24x24 16x16", - "type": "image/x-icon" - } - ], - "start_url": ".", - "display": "standalone", - "theme_color": "#000000", - "background_color": "#ffffff" -} \ No newline at end of file From 4a7a8df5f78111298df15a384158e2c5d1b8ae50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C4=B1ld=C4=B1ray=20=C3=9Cnl=C3=BC?= Date: Mon, 16 Jan 2023 15:41:30 +0300 Subject: [PATCH 12/29] chore: rename example --- .../.gitignore | 0 .../README.md | 4 ++-- .../package.json | 2 +- .../public/favicon.ico | Bin .../public/index.html | 0 .../public/manifest.json | 0 .../src/App.css | 0 .../src/App.tsx | 0 .../src/index.tsx | 0 .../src/interfaces/index.d.ts | 0 .../src/pages/posts/list.tsx | 0 .../src/react-app-env.d.ts | 0 .../tsconfig.json | 0 13 files changed, 3 insertions(+), 3 deletions(-) rename examples/{data-use-infinite-list => use-infinite-list}/.gitignore (100%) rename examples/{data-use-infinite-list => use-infinite-list}/README.md (91%) rename examples/{data-use-infinite-list => use-infinite-list}/package.json (96%) rename examples/{data-use-infinite-list => use-infinite-list}/public/favicon.ico (100%) rename examples/{data-use-infinite-list => use-infinite-list}/public/index.html (100%) rename examples/{data-use-infinite-list => use-infinite-list}/public/manifest.json (100%) rename examples/{data-use-infinite-list => use-infinite-list}/src/App.css (100%) rename examples/{data-use-infinite-list => use-infinite-list}/src/App.tsx (100%) rename examples/{data-use-infinite-list => use-infinite-list}/src/index.tsx (100%) rename examples/{data-use-infinite-list => use-infinite-list}/src/interfaces/index.d.ts (100%) rename examples/{data-use-infinite-list => use-infinite-list}/src/pages/posts/list.tsx (100%) rename examples/{data-use-infinite-list => use-infinite-list}/src/react-app-env.d.ts (100%) rename examples/{data-use-infinite-list => use-infinite-list}/tsconfig.json (100%) diff --git a/examples/data-use-infinite-list/.gitignore b/examples/use-infinite-list/.gitignore similarity index 100% rename from examples/data-use-infinite-list/.gitignore rename to examples/use-infinite-list/.gitignore diff --git a/examples/data-use-infinite-list/README.md b/examples/use-infinite-list/README.md similarity index 91% rename from examples/data-use-infinite-list/README.md rename to examples/use-infinite-list/README.md index 1957fdca5dde..fead4f67f5b5 100644 --- a/examples/data-use-infinite-list/README.md +++ b/examples/use-infinite-list/README.md @@ -34,12 +34,12 @@ ## Try it out on your local ```bash -npm create refine-app@latest -- --example data-use-infinite-list +npm create refine-app@latest -- --example use-infinite-list ``` ## Try it out on StackBlitz
    -[![Open useInfiniteList hook example from refine](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/refinedev/refine/tree/master/examples/data-use-infinite-list?terminal=start&preset=node) +[![Open useInfiniteList hook example from refine](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/refinedev/refine/tree/master/examples/use-infinite-list?terminal=start&preset=node) diff --git a/examples/data-use-infinite-list/package.json b/examples/use-infinite-list/package.json similarity index 96% rename from examples/data-use-infinite-list/package.json rename to examples/use-infinite-list/package.json index 01f496599f02..e4d525d859d4 100644 --- a/examples/data-use-infinite-list/package.json +++ b/examples/use-infinite-list/package.json @@ -1,5 +1,5 @@ { - "name": "data-use-infinite-list", + "name": "use-infinite-list", "version": "3.25.0", "private": true, "dependencies": { diff --git a/examples/data-use-infinite-list/public/favicon.ico b/examples/use-infinite-list/public/favicon.ico similarity index 100% rename from examples/data-use-infinite-list/public/favicon.ico rename to examples/use-infinite-list/public/favicon.ico diff --git a/examples/data-use-infinite-list/public/index.html b/examples/use-infinite-list/public/index.html similarity index 100% rename from examples/data-use-infinite-list/public/index.html rename to examples/use-infinite-list/public/index.html diff --git a/examples/data-use-infinite-list/public/manifest.json b/examples/use-infinite-list/public/manifest.json similarity index 100% rename from examples/data-use-infinite-list/public/manifest.json rename to examples/use-infinite-list/public/manifest.json diff --git a/examples/data-use-infinite-list/src/App.css b/examples/use-infinite-list/src/App.css similarity index 100% rename from examples/data-use-infinite-list/src/App.css rename to examples/use-infinite-list/src/App.css diff --git a/examples/data-use-infinite-list/src/App.tsx b/examples/use-infinite-list/src/App.tsx similarity index 100% rename from examples/data-use-infinite-list/src/App.tsx rename to examples/use-infinite-list/src/App.tsx diff --git a/examples/data-use-infinite-list/src/index.tsx b/examples/use-infinite-list/src/index.tsx similarity index 100% rename from examples/data-use-infinite-list/src/index.tsx rename to examples/use-infinite-list/src/index.tsx diff --git a/examples/data-use-infinite-list/src/interfaces/index.d.ts b/examples/use-infinite-list/src/interfaces/index.d.ts similarity index 100% rename from examples/data-use-infinite-list/src/interfaces/index.d.ts rename to examples/use-infinite-list/src/interfaces/index.d.ts diff --git a/examples/data-use-infinite-list/src/pages/posts/list.tsx b/examples/use-infinite-list/src/pages/posts/list.tsx similarity index 100% rename from examples/data-use-infinite-list/src/pages/posts/list.tsx rename to examples/use-infinite-list/src/pages/posts/list.tsx diff --git a/examples/data-use-infinite-list/src/react-app-env.d.ts b/examples/use-infinite-list/src/react-app-env.d.ts similarity index 100% rename from examples/data-use-infinite-list/src/react-app-env.d.ts rename to examples/use-infinite-list/src/react-app-env.d.ts diff --git a/examples/data-use-infinite-list/tsconfig.json b/examples/use-infinite-list/tsconfig.json similarity index 100% rename from examples/data-use-infinite-list/tsconfig.json rename to examples/use-infinite-list/tsconfig.json From 6e248f51feac1cc2d1df66f3a4ac38d6d36eeee4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C4=B1ld=C4=B1ray=20=C3=9Cnl=C3=BC?= Date: Mon, 16 Jan 2023 16:28:24 +0300 Subject: [PATCH 13/29] feat: add pageInfo param for GetListResponse type --- packages/core/src/contexts/data/IDataContext.ts | 4 ++++ packages/core/src/hooks/data/useInfiniteList.ts | 17 +++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/core/src/contexts/data/IDataContext.ts b/packages/core/src/contexts/data/IDataContext.ts index 5132a0fb789d..62efeaa9c118 100644 --- a/packages/core/src/contexts/data/IDataContext.ts +++ b/packages/core/src/contexts/data/IDataContext.ts @@ -87,6 +87,10 @@ export interface CustomResponse { export interface GetListResponse { data: TData[]; total: number; + pageInfo?: { + hasNextPage: boolean; + hasPreviousPage: boolean; + }; } export interface GetInfinityListResponse diff --git a/packages/core/src/hooks/data/useInfiniteList.ts b/packages/core/src/hooks/data/useInfiniteList.ts index 77b881b92a91..938291942ae4 100644 --- a/packages/core/src/hooks/data/useInfiniteList.ts +++ b/packages/core/src/hooks/data/useInfiniteList.ts @@ -149,11 +149,12 @@ export const useInfiniteList = < signal, }, }, - }).then(({ data, total }) => { + }).then(({ data, total, pageInfo }) => { return { data, total, pagination, + pageInfo, }; }); }, @@ -194,17 +195,25 @@ export const useInfiniteList = < }); }, getNextPageParam: (lastPage) => { - const { pagination } = lastPage; + const { pagination, pageInfo } = lastPage; const current = pagination?.current || 1; - const pageSize = pagination?.pageSize || 10; + if (pageInfo) { + return pageInfo.hasNextPage ? current + 1 : undefined; + } + + const pageSize = pagination?.pageSize || 10; const totalPages = Math.ceil((lastPage.total || 0) / pageSize); return current < totalPages ? Number(current) + 1 : undefined; }, getPreviousPageParam: (lastPage) => { - const { pagination } = lastPage; + const { pagination, pageInfo } = lastPage; const current = pagination?.current || 1; + if (pageInfo) { + return pageInfo.hasPreviousPage ? current - 1 : undefined; + } + return current === 1 ? undefined : current - 1; }, }, From 25b23a3e2cd964489e9755416c23255f58ce1db4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C4=B1ld=C4=B1ray=20=C3=9Cnl=C3=BC?= Date: Mon, 16 Jan 2023 22:16:15 +0300 Subject: [PATCH 14/29] refactor: add useInfinitePagination helper --- .../core/src/definitions/helpers/index.ts | 4 ++ .../useInfinitePagination/index.spec.ts | 55 +++++++++++++++++++ .../helpers/useInfinitePagination/index.ts | 26 +++++++++ .../core/src/hooks/data/useInfiniteList.ts | 31 +++-------- 4 files changed, 93 insertions(+), 23 deletions(-) create mode 100644 packages/core/src/definitions/helpers/useInfinitePagination/index.spec.ts create mode 100644 packages/core/src/definitions/helpers/useInfinitePagination/index.ts diff --git a/packages/core/src/definitions/helpers/index.ts b/packages/core/src/definitions/helpers/index.ts index 34c8318640e3..bb53814bf8d8 100644 --- a/packages/core/src/definitions/helpers/index.ts +++ b/packages/core/src/definitions/helpers/index.ts @@ -12,3 +12,7 @@ export { redirectPage } from "./redirectPage"; export { sequentialPromises } from "./sequentialPromises"; export { pickDataProvider } from "./pickDataProvider"; export { handleMultiple } from "./handleMultiple"; +export { + getNextPageParam, + getPreviousPageParam, +} from "./useInfinitePagination"; diff --git a/packages/core/src/definitions/helpers/useInfinitePagination/index.spec.ts b/packages/core/src/definitions/helpers/useInfinitePagination/index.spec.ts new file mode 100644 index 000000000000..e14ebcedef77 --- /dev/null +++ b/packages/core/src/definitions/helpers/useInfinitePagination/index.spec.ts @@ -0,0 +1,55 @@ +import { getNextPageParam, getPreviousPageParam } from "./index"; + +describe("useInfiniteList pagination helper", () => { + describe("getNextPageParam", () => { + it("default page size", () => { + const hasNextPage = getNextPageParam({ + data: [], + total: 10, + }); + expect(hasNextPage).toBe(undefined); + }); + it("custom pageSize and current page", () => { + const hasNextPage = getNextPageParam({ + data: [], + total: 10, + pagination: { + current: 2, + pageSize: 3, + }, + }); + expect(hasNextPage).toBe(3); + }); + it("hasNextPage true", () => { + const hasNextPage = getNextPageParam({ + data: [], + total: 10, + pageInfo: { + hasNextPage: true, + hasPreviousPage: false, + }, + }); + expect(hasNextPage).toBe(2); + }); + }); + describe("getPreviousPageParam", () => { + it("custom pageSize and current page", () => { + const hasPreviousPage = getPreviousPageParam({ + data: [], + total: 10, + pagination: { + current: 2, + pageSize: 3, + }, + }); + expect(hasPreviousPage).toBe(1); + }); + it("hasPreviousPage false", () => { + const hasPreviousPage = getPreviousPageParam({ + data: [], + total: 10, + }); + expect(hasPreviousPage).toBe(undefined); + }); + }); +}); diff --git a/packages/core/src/definitions/helpers/useInfinitePagination/index.ts b/packages/core/src/definitions/helpers/useInfinitePagination/index.ts new file mode 100644 index 000000000000..6e1bd42195e5 --- /dev/null +++ b/packages/core/src/definitions/helpers/useInfinitePagination/index.ts @@ -0,0 +1,26 @@ +import { GetInfinityListResponse } from "../../../interfaces"; + +export const getNextPageParam = (lastPage: GetInfinityListResponse) => { + const { pagination, pageInfo } = lastPage; + const current = pagination?.current || 1; + + if (pageInfo) { + return pageInfo.hasNextPage ? current + 1 : undefined; + } + + const pageSize = pagination?.pageSize || 10; + const totalPages = Math.ceil((lastPage.total || 0) / pageSize); + + return current < totalPages ? Number(current) + 1 : undefined; +}; + +export const getPreviousPageParam = (lastPage: GetInfinityListResponse) => { + const { pagination, pageInfo } = lastPage; + const current = pagination?.current || 1; + + if (pageInfo) { + return pageInfo.hasPreviousPage ? current - 1 : undefined; + } + + return current === 1 ? undefined : current - 1; +}; diff --git a/packages/core/src/hooks/data/useInfiniteList.ts b/packages/core/src/hooks/data/useInfiniteList.ts index 938291942ae4..c1c88dd11a1e 100644 --- a/packages/core/src/hooks/data/useInfiniteList.ts +++ b/packages/core/src/hooks/data/useInfiniteList.ts @@ -22,7 +22,12 @@ import { useTranslate, useDataProvider, } from "@hooks"; -import { queryKeys, pickDataProvider } from "@definitions/helpers"; +import { + queryKeys, + pickDataProvider, + getNextPageParam, + getPreviousPageParam, +} from "@definitions/helpers"; export interface UseInfinityConfig { pagination?: Pagination; @@ -194,28 +199,8 @@ export const useInfiniteList = < type: "error", }); }, - getNextPageParam: (lastPage) => { - const { pagination, pageInfo } = lastPage; - const current = pagination?.current || 1; - - if (pageInfo) { - return pageInfo.hasNextPage ? current + 1 : undefined; - } - - const pageSize = pagination?.pageSize || 10; - const totalPages = Math.ceil((lastPage.total || 0) / pageSize); - return current < totalPages ? Number(current) + 1 : undefined; - }, - getPreviousPageParam: (lastPage) => { - const { pagination, pageInfo } = lastPage; - const current = pagination?.current || 1; - - if (pageInfo) { - return pageInfo.hasPreviousPage ? current - 1 : undefined; - } - - return current === 1 ? undefined : current - 1; - }, + getNextPageParam: (lastPage) => getNextPageParam(lastPage), + getPreviousPageParam: (lastPage) => getPreviousPageParam(lastPage), }, ); From cd06d583a24dd0aafcea99b21fdadba03bc32d11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C4=B1ld=C4=B1ray=20=C3=9Cnl=C3=BC?= Date: Tue, 17 Jan 2023 09:23:49 +0300 Subject: [PATCH 15/29] chore: rename GetInfinityListResponse to GetInfiniteListResponse type --- packages/core/src/contexts/data/IDataContext.ts | 2 +- .../definitions/helpers/useInfinitePagination/index.ts | 6 +++--- packages/core/src/hooks/data/useInfiniteList.ts | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/core/src/contexts/data/IDataContext.ts b/packages/core/src/contexts/data/IDataContext.ts index 62efeaa9c118..e77cf9f043ea 100644 --- a/packages/core/src/contexts/data/IDataContext.ts +++ b/packages/core/src/contexts/data/IDataContext.ts @@ -93,7 +93,7 @@ export interface GetListResponse { }; } -export interface GetInfinityListResponse +export interface GetInfiniteListResponse extends GetListResponse { pagination?: Pagination; } diff --git a/packages/core/src/definitions/helpers/useInfinitePagination/index.ts b/packages/core/src/definitions/helpers/useInfinitePagination/index.ts index 6e1bd42195e5..3a94c0263d7a 100644 --- a/packages/core/src/definitions/helpers/useInfinitePagination/index.ts +++ b/packages/core/src/definitions/helpers/useInfinitePagination/index.ts @@ -1,6 +1,6 @@ -import { GetInfinityListResponse } from "../../../interfaces"; +import { GetInfiniteListResponse } from "../../../interfaces"; -export const getNextPageParam = (lastPage: GetInfinityListResponse) => { +export const getNextPageParam = (lastPage: GetInfiniteListResponse) => { const { pagination, pageInfo } = lastPage; const current = pagination?.current || 1; @@ -14,7 +14,7 @@ export const getNextPageParam = (lastPage: GetInfinityListResponse) => { return current < totalPages ? Number(current) + 1 : undefined; }; -export const getPreviousPageParam = (lastPage: GetInfinityListResponse) => { +export const getPreviousPageParam = (lastPage: GetInfiniteListResponse) => { const { pagination, pageInfo } = lastPage; const current = pagination?.current || 1; diff --git a/packages/core/src/hooks/data/useInfiniteList.ts b/packages/core/src/hooks/data/useInfiniteList.ts index c1c88dd11a1e..27297100037f 100644 --- a/packages/core/src/hooks/data/useInfiniteList.ts +++ b/packages/core/src/hooks/data/useInfiniteList.ts @@ -12,7 +12,7 @@ import { MetaDataQuery, SuccessErrorNotification, LiveModeProps, - GetInfinityListResponse, + GetInfiniteListResponse, } from "../../interfaces"; import { useResource, @@ -50,7 +50,7 @@ export type UseInfinityListProps = { * react-query's [useInfiniteQuery](https://tanstack.com/query/v4/docs/react/reference/useInfiniteQuery) options, */ queryOptions?: UseInfiniteQueryOptions< - GetInfinityListResponse, + GetInfiniteListResponse, TError >; /** @@ -90,7 +90,7 @@ export const useInfiniteList = < liveParams, dataProviderName, }: UseInfinityListProps): InfiniteQueryObserverResult< - GetInfinityListResponse, + GetInfiniteListResponse, TError > => { const { resources } = useResource(); @@ -130,7 +130,7 @@ export const useInfiniteList = < }); const queryResponse = useInfiniteQuery< - GetInfinityListResponse, + GetInfiniteListResponse, TError >( queryKey.list(config), From 496d45a3891f893d4be7caf133f88bcf9afda4f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C4=B1ld=C4=B1ray=20=C3=9Cnl=C3=BC?= Date: Tue, 17 Jan 2023 09:26:50 +0300 Subject: [PATCH 16/29] chore: rename *Infinity to *Infinite --- packages/core/src/hooks/data/useInfiniteList.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/src/hooks/data/useInfiniteList.ts b/packages/core/src/hooks/data/useInfiniteList.ts index 27297100037f..e054876baa53 100644 --- a/packages/core/src/hooks/data/useInfiniteList.ts +++ b/packages/core/src/hooks/data/useInfiniteList.ts @@ -29,23 +29,23 @@ import { getPreviousPageParam, } from "@definitions/helpers"; -export interface UseInfinityConfig { +export interface UseInfiniteListConfig { pagination?: Pagination; hasPagination?: boolean; sort?: CrudSorting; filters?: CrudFilters; } -export type UseInfinityListProps = { +export type UseInfiniteListProps = { /** * Resource name for API data interactions */ resource: string; /** * Configuration for pagination, sorting and filtering - * @type [`UseInfinityConfig`](/docs/api-reference/core/hooks/data/useInfiniteList/#config-parameters) + * @type [`useInfiniteListConfig`](/docs/api-reference/core/hooks/data/useInfiniteList/#config-parameters) */ - config?: UseInfinityConfig; + config?: UseInfiniteListConfig; /** * react-query's [useInfiniteQuery](https://tanstack.com/query/v4/docs/react/reference/useInfiniteQuery) options, */ @@ -89,7 +89,7 @@ export const useInfiniteList = < onLiveEvent, liveParams, dataProviderName, -}: UseInfinityListProps): InfiniteQueryObserverResult< +}: UseInfiniteListProps): InfiniteQueryObserverResult< GetInfiniteListResponse, TError > => { From 8ce8f74bf79e6160dea896b8d411454b27561558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C4=B1ld=C4=B1ray=20=C3=9Cnl=C3=BC?= Date: Tue, 17 Jan 2023 16:03:35 +0300 Subject: [PATCH 17/29] docs: add base useInfiniteList --- .../core/hooks/data/useInfiniteList.md | 303 ++++++++++++++++++ documentation/sidebars.js | 1 + 2 files changed, 304 insertions(+) create mode 100644 documentation/docs/api-reference/core/hooks/data/useInfiniteList.md diff --git a/documentation/docs/api-reference/core/hooks/data/useInfiniteList.md b/documentation/docs/api-reference/core/hooks/data/useInfiniteList.md new file mode 100644 index 000000000000..89bf01ea90a5 --- /dev/null +++ b/documentation/docs/api-reference/core/hooks/data/useInfiniteList.md @@ -0,0 +1,303 @@ +--- +id: useInfiniteList +title: useInfiniteList +siderbar_label: useInfiniteList +description: useInfiniteList data hook from refine is a modified version of react-query's useInfiniteQuery for retrieving items from a resource with pagination, search, sort, and filter configurations. +--- + +In some APIs, the `cursor-pagination` method is used for its various advantages. With **refine**, this type of API's can be retrieved with the [`useInfiniteQuery`](https://tanstack.com/query/v4/docs/react/reference/useInfiniteQuery) data hook, pagination, filtering and sorting can be done. Ideal for fetching data of the next page and user with a button (or scroll listener) where the total number of records is unknown. + +It uses the `getList` method as the query function from the [`dataProvider`](/api-reference/core/providers/data-provider.md) which is passed to ``. + +*TODO: Add LivePreview* + +## Usage + +There is an API where the pagination is done with `cursor` where the total number of pages is unknown as below. + +```ts title="https://api.fake-rest.refine.dev/posts" +{ + posts: [ + { + id: 1, + title: "E-business", + status: "draft", + }, + { + id: 2, + title: "Virtual Invoice Avon", + status: "published", + } + ], + cursor: { + next: 2, + prev: null, + }, +} +``` + +Now let's see how to use this API with `useInfiniteQuery`. + + +```tsx +import { useInfiniteList } from "@pankod/refine-core"; + +const { + data, + error, + hasNextPage, + isLoading, + fetchNextPage, + isFetchingNextPage, +} = useInfiniteList({ + resource: "products", +}); + +if (isLoading) { + return

    Loading

    ; +} +if (error) { + return

    ERROR

    ; +} + +return ( +
    + {data?.pages.map((page) => + page.data.map((product) => ( +
  • + {product.id}-{product.name} +
  • + )), + )} + + +
    +); +``` + +
    + +Although we didn't pass any sort order configurations to `useList`, data comes in descending order according to `id` since `getList` has default values for sort order: + +```ts +{ + sort: [{ order: "desc", field: "id" }]; +} +``` + +:::caution +`getList` also has default values for pagination: + +```ts +{ + pagination: { current: 1, pageSize: 10 } +} +``` + +::: +:::caution + +If you want to create your own `getList` method, it will automatically implement default query configurations since `useList` can work with no configuration parameters. + +::: + +
    + +### Query Configuration + +#### `pagination` + +Allows us to set page and items per page values. + +For example imagine that we have 1000 post records: + +```ts +import { useList } from "@pankod/refine-core"; + +const postListQueryResult = useList({ + resource: "posts", + config: { + pagination: { current: 3, pageSize: 8 }, + }, +}); +``` + +> Listing will start from page 3 showing 8 records. + +
    + +#### `sort` + +Allows us to sort records by the speficified order and field. + +```ts +import { useList } from "@pankod/refine-core"; + +const postListQueryResult = useList({ + resource: "posts", + config: { + sort: [{ order: "asc", field: "title" }], + }, +}); +``` + +```ts title="postListQueryResult.data" +{ + data: [ + { + id: 1, + title: "E-business", + status: "draft" + }, + { + id: 3, + title: "Powerful Crypto", + status: "rejected" + }, + { + id: 2, + title: "Virtual Invoice Avon", + status: "published" + }, + ], + total: 3 +} +``` + +> Listing starts from ascending alphabetical order on the `title` field. + +
    + +#### `filters` + +Allows us to filter queries using refine's filter operators. It is configured via `field`, `operator` and `value` properites. + +```ts +import { useList } from "@pankod/refine-core"; + +const postListQueryResult = useList({ + resource: "posts", + config: { + filters: [ + { + field: "status", + operator: "eq", + value: "rejected", + }, + ], + }, +}); +``` + +```ts title="postListQueryResult.data" +{ + data: [ + { + id: 3, + title: "Powerful Crypto", + status: "rejected" + }, + ], + total: 1 +} +``` + +> Only lists records whose `status` equals to "rejected". + +
    + +**Supported operators** + +| Filter | Description | +| ------------ | ------------------------------- | +| `eq` | Equal | +| `ne` | Not equal | +| `lt` | Less than | +| `gt` | Greater than | +| `lte` | Less than or equal to | +| `gte` | Greater than or equal to | +| `in` | Included in an array | +| `nin` | Not included in an array | +| `contains` | Contains | +| `ncontains` | Doesn't contain | +| `containss` | Contains, case sensitive | +| `ncontainss` | Doesn't contain, case sensitive | +| `null` | Is null or not null | + +
    + +:::tip +`useList` can also accept all `useQuery` options as a third parameter. +[Refer to react-query docs for further information. →](https://react-query.tanstack.com/reference/useQuery) + +- For example, to disable query from running automatically you can set `enabled` to `false`. + +```ts +import { useList } from "@pankod/refine-core"; + +const postListQueryResult = useList({ + resource: "posts", + queryOptions: { enabled: false }, +}); +``` + +::: + +
    + +:::tip +`useList` returns the result of `react-query`'s `useQuery` which includes many properties such as `isLoading` and `isFetching`. +[Refer to react-query docs for further information. →](https://react-query.tanstack.com/reference/useQuery) +::: + +## API + +### Properties + + + +### Config parameters + +```ts +interface UseListConfig { + hasPagination?: boolean; + pagination?: { + current?: number; + pageSize?: number; + }; + sort?: Array<{ + field: string; + order: "asc" | "desc"; + }>; + filters?: Array<{ + field: string; + operator: CrudOperators; + value: any; + }>; +} +``` + +### Type Parameters + +| Property | Desription | Type | Default | +| -------- | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| TData | Result data of the query. Extends [`BaseRecord`](/api-reference/core/interfaces.md#baserecord) | [`BaseRecord`](/api-reference/core/interfaces.md#baserecord) | [`BaseRecord`](/api-reference/core/interfaces.md#baserecord) | +| TError | Custom error object that extends [`HttpError`](/api-reference/core/interfaces.md#httperror) | [`HttpError`](/api-reference/core/interfaces.md#httperror) | [`HttpError`](/api-reference/core/interfaces.md#httperror) | + +### Return values + +| Description | Type | +| ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | +| Result of the `react-query`'s `useQuery` | [`QueryObserverResult<{`
    ` data: TData[];`
    ` total: number; },`
    ` TError>`](https://react-query.tanstack.com/reference/useQuery) | diff --git a/documentation/sidebars.js b/documentation/sidebars.js index b6a3abc12764..9e6d4f8b7597 100644 --- a/documentation/sidebars.js +++ b/documentation/sidebars.js @@ -115,6 +115,7 @@ module.exports = { "api-reference/core/hooks/data/useDelete", "api-reference/core/hooks/data/useDeleteMany", "api-reference/core/hooks/data/useList", + "api-reference/core/hooks/data/useInfiniteList", "api-reference/core/hooks/data/useMany", "api-reference/core/hooks/data/useOne", "api-reference/core/hooks/data/useUpdate", From 804a8b536438c9d108701ffe834a5e48a743b905 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C4=B1ld=C4=B1ray=20=C3=9Cnl=C3=BC?= Date: Wed, 18 Jan 2023 15:26:27 +0300 Subject: [PATCH 18/29] refactor: getNextPageParam method --- .../core/src/contexts/data/IDataContext.ts | 11 ++------ .../helpers/useInfinitePagination/index.ts | 26 +++++++++++-------- .../core/src/hooks/data/useInfiniteList.ts | 22 ++++++---------- packages/core/src/index.tsx | 2 ++ 4 files changed, 27 insertions(+), 34 deletions(-) diff --git a/packages/core/src/contexts/data/IDataContext.ts b/packages/core/src/contexts/data/IDataContext.ts index e77cf9f043ea..0336e6ad8b85 100644 --- a/packages/core/src/contexts/data/IDataContext.ts +++ b/packages/core/src/contexts/data/IDataContext.ts @@ -87,15 +87,7 @@ export interface CustomResponse { export interface GetListResponse { data: TData[]; total: number; - pageInfo?: { - hasNextPage: boolean; - hasPreviousPage: boolean; - }; -} - -export interface GetInfiniteListResponse - extends GetListResponse { - pagination?: Pagination; + [key: string]: any; } export interface CreateResponse { @@ -139,6 +131,7 @@ export interface IDataContextProvider { filters?: CrudFilters; metaData?: MetaDataQuery; dataProviderName?: string; + cursor?: unknown; }) => Promise>; getMany?: (params: { resource: string; diff --git a/packages/core/src/definitions/helpers/useInfinitePagination/index.ts b/packages/core/src/definitions/helpers/useInfinitePagination/index.ts index 3a94c0263d7a..bf1f6964425b 100644 --- a/packages/core/src/definitions/helpers/useInfinitePagination/index.ts +++ b/packages/core/src/definitions/helpers/useInfinitePagination/index.ts @@ -1,26 +1,30 @@ -import { GetInfiniteListResponse } from "../../../interfaces"; +import { GetListResponse } from "../../../interfaces"; -export const getNextPageParam = (lastPage: GetInfiniteListResponse) => { - const { pagination, pageInfo } = lastPage; - const current = pagination?.current || 1; +export const getNextPageParam = (lastPage: GetListResponse) => { + const { pagination, cursor } = lastPage; - if (pageInfo) { - return pageInfo.hasNextPage ? current + 1 : undefined; + // cursor pagination + if (cursor && cursor.next) { + return cursor.next; } + const current = pagination?.current || 1; + const pageSize = pagination?.pageSize || 10; const totalPages = Math.ceil((lastPage.total || 0) / pageSize); return current < totalPages ? Number(current) + 1 : undefined; }; -export const getPreviousPageParam = (lastPage: GetInfiniteListResponse) => { - const { pagination, pageInfo } = lastPage; - const current = pagination?.current || 1; +export const getPreviousPageParam = (lastPage: GetListResponse) => { + const { pagination, cursor } = lastPage; - if (pageInfo) { - return pageInfo.hasPreviousPage ? current - 1 : undefined; + // cursor pagination + if (cursor && cursor.prev) { + return cursor.prev; } + const current = pagination?.current || 1; + return current === 1 ? undefined : current - 1; }; diff --git a/packages/core/src/hooks/data/useInfiniteList.ts b/packages/core/src/hooks/data/useInfiniteList.ts index e054876baa53..26a6857f2851 100644 --- a/packages/core/src/hooks/data/useInfiniteList.ts +++ b/packages/core/src/hooks/data/useInfiniteList.ts @@ -12,7 +12,7 @@ import { MetaDataQuery, SuccessErrorNotification, LiveModeProps, - GetInfiniteListResponse, + GetListResponse, } from "../../interfaces"; import { useResource, @@ -49,10 +49,7 @@ export type UseInfiniteListProps = { /** * react-query's [useInfiniteQuery](https://tanstack.com/query/v4/docs/react/reference/useInfiniteQuery) options, */ - queryOptions?: UseInfiniteQueryOptions< - GetInfiniteListResponse, - TError - >; + queryOptions?: UseInfiniteQueryOptions, TError>; /** * Metadata query for `dataProvider` */ @@ -90,7 +87,7 @@ export const useInfiniteList = < liveParams, dataProviderName, }: UseInfiniteListProps): InfiniteQueryObserverResult< - GetInfiniteListResponse, + GetListResponse, TError > => { const { resources } = useResource(); @@ -129,10 +126,7 @@ export const useInfiniteList = < onLiveEvent, }); - const queryResponse = useInfiniteQuery< - GetInfiniteListResponse, - TError - >( + const queryResponse = useInfiniteQuery, TError>( queryKey.list(config), ({ queryKey, pageParam, signal }) => { const { hasPagination, ...restConfig } = config || {}; @@ -154,16 +148,18 @@ export const useInfiniteList = < signal, }, }, - }).then(({ data, total, pageInfo }) => { + }).then(({ data, total, ...rest }) => { return { data, total, pagination, - pageInfo, + ...rest, }; }); }, { + getNextPageParam: (lastPage) => getNextPageParam(lastPage), + getPreviousPageParam: (lastPage) => getPreviousPageParam(lastPage), ...queryOptions, onSuccess: (data) => { queryOptions?.onSuccess?.(data); @@ -199,8 +195,6 @@ export const useInfiniteList = < type: "error", }); }, - getNextPageParam: (lastPage) => getNextPageParam(lastPage), - getPreviousPageParam: (lastPage) => getPreviousPageParam(lastPage), }, ); diff --git a/packages/core/src/index.tsx b/packages/core/src/index.tsx index d7caa48d6de6..23c9c8b2b910 100644 --- a/packages/core/src/index.tsx +++ b/packages/core/src/index.tsx @@ -93,5 +93,7 @@ export { importCSVMapper, routeGenerator, userFriendlyResourceName, + getNextPageParam, + getPreviousPageParam, } from "./definitions/helpers"; export { file2Base64 } from "./definitions/upload"; From 224abaa258a27bae3b3d5dfa426016dd2fa6bac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C4=B1ld=C4=B1ray=20=C3=9Cnl=C3=BC?= Date: Wed, 18 Jan 2023 15:26:43 +0300 Subject: [PATCH 19/29] chore: update example --- examples/use-infinite-list/src/App.tsx | 38 ++++++++++++- .../src/github-data-provider/index.ts | 55 ++++++++++++++++++ .../src/github-data-provider/utils/axios.ts | 23 ++++++++ .../src/github-data-provider/utils/index.ts | 1 + .../src/interfaces/index.d.ts | 7 +-- .../src/pages/commits/list.tsx | 57 +++++++++++++++++++ .../src/pages/posts/list.tsx | 24 ++++---- 7 files changed, 188 insertions(+), 17 deletions(-) create mode 100644 examples/use-infinite-list/src/github-data-provider/index.ts create mode 100644 examples/use-infinite-list/src/github-data-provider/utils/axios.ts create mode 100644 examples/use-infinite-list/src/github-data-provider/utils/index.ts create mode 100644 examples/use-infinite-list/src/pages/commits/list.tsx diff --git a/examples/use-infinite-list/src/App.tsx b/examples/use-infinite-list/src/App.tsx index 4593610b5d17..54aad2f6cf0c 100644 --- a/examples/use-infinite-list/src/App.tsx +++ b/examples/use-infinite-list/src/App.tsx @@ -1,20 +1,52 @@ -import { Refine } from "@pankod/refine-core"; -import routerProvider from "@pankod/refine-react-router-v6"; +import { LayoutProps, Refine } from "@pankod/refine-core"; +import routerProvider, { Link } from "@pankod/refine-react-router-v6"; import dataProvider from "@pankod/refine-simple-rest"; +import { githubDataProvider } from "github-data-provider"; import { PostList } from "pages/posts/list"; +import { CommitList } from "pages/commits/list"; import "./App.css"; +const Layout: React.FunctionComponent = ({ children }) => { + return ( +
    +
      +
    • + + Post example with Simple Rest (offset pagination) + +
    • +
    • + + Commits example with Github API Data Provider (cursor + pagination) + +
    • +
    + + {children} +
    + ); +}; + const App: React.FC = () => { return ( ); diff --git a/examples/use-infinite-list/src/github-data-provider/index.ts b/examples/use-infinite-list/src/github-data-provider/index.ts new file mode 100644 index 000000000000..1299704da6db --- /dev/null +++ b/examples/use-infinite-list/src/github-data-provider/index.ts @@ -0,0 +1,55 @@ +import { AxiosInstance } from "axios"; +import { DataProvider } from "@pankod/refine-core"; +import { axiosInstance } from "./utils"; + +/* + * Cursor pagination is left simple for example. + */ + +export const githubDataProvider = ( + httpClient: AxiosInstance = axiosInstance, +): Omit< + Required, + "createMany" | "updateMany" | "deleteMany" +> => ({ + getList: async ({ resource, pagination }) => { + const { data } = await httpClient.get( + `https://api.github.com/repos/refinedev/refine/${resource}?until=${ + pagination?.current || new Date().toISOString() + }`, + ); + + return { + data, + total: 0, + }; + }, + + getMany: async () => { + throw new Error("Method not implemented."); + }, + + create: async () => { + throw new Error("Method not implemented."); + }, + + update: async () => { + throw new Error("Method not implemented."); + }, + + getOne: async () => { + throw new Error("Method not implemented."); + }, + + deleteOne: async () => { + throw new Error("Method not implemented."); + }, + + getApiUrl: () => { + return "https://api.github.com"; + }, + + custom: async () => { + throw new Error("Method not implemented."); + }, +}); diff --git a/examples/use-infinite-list/src/github-data-provider/utils/axios.ts b/examples/use-infinite-list/src/github-data-provider/utils/axios.ts new file mode 100644 index 000000000000..4098c943eb11 --- /dev/null +++ b/examples/use-infinite-list/src/github-data-provider/utils/axios.ts @@ -0,0 +1,23 @@ +import { HttpError } from "@pankod/refine-core"; + +// "axios" package should be installed to customize the http client +import axios from "axios"; + +const axiosInstance = axios.create(); + +axiosInstance.interceptors.response.use( + (response) => { + return response; + }, + (error) => { + const customError: HttpError = { + ...error, + message: error.response?.data?.message, + statusCode: error.response?.status, + }; + + return Promise.reject(customError); + }, +); + +export { axiosInstance }; diff --git a/examples/use-infinite-list/src/github-data-provider/utils/index.ts b/examples/use-infinite-list/src/github-data-provider/utils/index.ts new file mode 100644 index 000000000000..fb9a82afe129 --- /dev/null +++ b/examples/use-infinite-list/src/github-data-provider/utils/index.ts @@ -0,0 +1 @@ +export { axiosInstance } from "./axios"; diff --git a/examples/use-infinite-list/src/interfaces/index.d.ts b/examples/use-infinite-list/src/interfaces/index.d.ts index 4513bec0b48d..161ccdc517c1 100644 --- a/examples/use-infinite-list/src/interfaces/index.d.ts +++ b/examples/use-infinite-list/src/interfaces/index.d.ts @@ -1,6 +1,5 @@ -export interface IProduct { +export interface IPost { id: number; - name: string; - material: string; - description: string; + title: string; + createdAt: string; } diff --git a/examples/use-infinite-list/src/pages/commits/list.tsx b/examples/use-infinite-list/src/pages/commits/list.tsx new file mode 100644 index 000000000000..74e382b5e7d3 --- /dev/null +++ b/examples/use-infinite-list/src/pages/commits/list.tsx @@ -0,0 +1,57 @@ +import React from "react"; +import { useInfiniteList } from "@pankod/refine-core"; + +export const CommitList: React.FC = () => { + const { + data, + error, + hasNextPage, + isLoading, + fetchNextPage, + isFetchingNextPage, + } = useInfiniteList({ + resource: "commits", + dataProviderName: "github", + queryOptions: { + getNextPageParam: ({ data }) => { + // return the last commit date of the last page + const lastCommit = data[data.length - 1]; + return lastCommit.commit.committer.date; + }, + }, + }); + + if (isLoading) { + return

    Loading

    ; + } + if (error) { + return

    ERROR

    ; + } + + return ( +
    +
      + {data?.pages.map((page) => + page.data.map(({ sha, commit }) => ( +
    • + {commit.committer.date} +
      + {commit.message} - {commit.committer.name} +
    • + )), + )} +
    + + +
    + ); +}; diff --git a/examples/use-infinite-list/src/pages/posts/list.tsx b/examples/use-infinite-list/src/pages/posts/list.tsx index 213cbf693b01..1e2ab2648ade 100644 --- a/examples/use-infinite-list/src/pages/posts/list.tsx +++ b/examples/use-infinite-list/src/pages/posts/list.tsx @@ -1,7 +1,7 @@ import React from "react"; import { useInfiniteList } from "@pankod/refine-core"; -import { IProduct } from "interfaces"; +import { IPost } from "interfaces"; export const PostList: React.FC = () => { const { @@ -11,8 +11,8 @@ export const PostList: React.FC = () => { isLoading, fetchNextPage, isFetchingNextPage, - } = useInfiniteList({ - resource: "products", + } = useInfiniteList({ + resource: "posts", }); if (isLoading) { @@ -24,13 +24,17 @@ export const PostList: React.FC = () => { return (
    - {data?.pages.map((page) => - page.data.map((product) => ( -
  • - {product.id}-{product.name} -
  • - )), - )} +
      + {data?.pages.map((page) => + page.data.map(({ id, title, createdAt }) => ( +
    • + {createdAt} +
      + {title} +
    • + )), + )} +
    +
      + {data?.pages.map((page) => + page.data.map(({ id, title, createdAt }) => ( +
    • + {createdAt} +
      + {title} +
    • + )), + )} +
    + + { + hasNextPage && ( + + ) + }
    ); ``` -
    - -Although we didn't pass any sort order configurations to `useList`, data comes in descending order according to `id` since `getList` has default values for sort order: - -```ts -{ - sort: [{ order: "desc", field: "id" }]; -} -``` +By default, `refine` expects you to return the `cursor` object, but is not required. This is because some APIs don't work that way. To fix this problem you need to override the `getNextPageParam` method and return the next `cursor`. -:::caution -`getList` also has default values for pagination: +```tsx +import { useInfiniteList } from "@pankod/refine-core"; -```ts -{ - pagination: { current: 1, pageSize: 10 } -} +const { + data, + error, + hasNextPage, + isLoading, + fetchNextPage, + isFetchingNextPage, +} = useInfiniteList({ + resource: "posts", + // highlight-start + queryOptions: { + getNextPageParam: (lastPage, allPages) => { + // return the last post's id + const { data } = lastPage; + const lastPost = data[data.length - 1]; + return lastPost.id; + }, + }, + // highlight-end +}); ``` - +:::tip +When you override this method, you can access the `lastPage` and `allPages`. ::: -:::caution -If you want to create your own `getList` method, it will automatically implement default query configurations since `useList` can work with no configuration parameters. +### Support for `offset-pagination` -::: +The pagination method prepared for the [`useList`](/docs/api-reference/core/hooks/data/useList.md) hook can also be used here. -
    +*TODO: More Information* ### Query Configuration @@ -121,9 +179,9 @@ Allows us to set page and items per page values. For example imagine that we have 1000 post records: ```ts -import { useList } from "@pankod/refine-core"; +import { useInfiniteList } from "@pankod/refine-core"; -const postListQueryResult = useList({ +const postListQueryResult = useInfiniteList({ resource: "posts", config: { pagination: { current: 3, pageSize: 8 }, @@ -140,9 +198,9 @@ const postListQueryResult = useList({ Allows us to sort records by the speficified order and field. ```ts -import { useList } from "@pankod/refine-core"; +import { useInfiniteList } from "@pankod/refine-core"; -const postListQueryResult = useList({ +const postListQueryResult = useInfiniteList({ resource: "posts", config: { sort: [{ order: "asc", field: "title" }], @@ -169,7 +227,6 @@ const postListQueryResult = useList({ status: "published" }, ], - total: 3 } ``` @@ -182,9 +239,9 @@ const postListQueryResult = useList({ Allows us to filter queries using refine's filter operators. It is configured via `field`, `operator` and `value` properites. ```ts -import { useList } from "@pankod/refine-core"; +import { useInfiniteList } from "@pankod/refine-core"; -const postListQueryResult = useList({ +const postListQueryResult = useInfiniteList({ resource: "posts", config: { filters: [ @@ -207,7 +264,6 @@ const postListQueryResult = useList({ status: "rejected" }, ], - total: 1 } ``` @@ -236,17 +292,25 @@ const postListQueryResult = useList({
    :::tip -`useList` can also accept all `useQuery` options as a third parameter. -[Refer to react-query docs for further information. →](https://react-query.tanstack.com/reference/useQuery) +`useInfiniteList` can also accept all `useInfiniteQuery` options as a third parameter. +[Refer to react-query docs for further information. →](https://react-query.tanstack.com/reference/useInfiniteQuery) - For example, to disable query from running automatically you can set `enabled` to `false`. ```ts -import { useList } from "@pankod/refine-core"; +import { useInfiniteList } from "@pankod/refine-core"; -const postListQueryResult = useList({ +const postListQueryResult = useInfiniteList({ resource: "posts", - queryOptions: { enabled: false }, + // highlight-start + queryOptions: { + enabled: false, + getNextPageParam: ({ data }) => { + const lastRow = data[data.length - 1]; + return lastRow.id; + }, + }, + // highlight-end }); ``` @@ -255,15 +319,15 @@ const postListQueryResult = useList({
    :::tip -`useList` returns the result of `react-query`'s `useQuery` which includes many properties such as `isLoading` and `isFetching`. -[Refer to react-query docs for further information. →](https://react-query.tanstack.com/reference/useQuery) +`useInfiniteList` returns the result of `react-query`'s `useInfiniteQuery` which includes many properties such as `fetchNextPage`, `hasNextPage` and `isFetchingNextPage`. +[Refer to react-query docs for further information. →](https://react-query.tanstack.com/reference/useInfiniteQuery) ::: ## API ### Properties - @@ -298,6 +362,6 @@ interface UseListConfig { ### Return values -| Description | Type | -| ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | -| Result of the `react-query`'s `useQuery` | [`QueryObserverResult<{`
    ` data: TData[];`
    ` total: number; },`
    ` TError>`](https://react-query.tanstack.com/reference/useQuery) | +| Description | Type | +| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Result of the `react-query`'s `useInfiniteQuery` | [`InfiniteQueryObserverResult<{`
    ` data: TData[];`
    ` total: number; },`
    ` TError>`](https://react-query.tanstack.com/reference/useInfiniteQuery) | From 3ac792ae0202b7457367b17f2ab83579fd39d57e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C4=B1ld=C4=B1ray=20=C3=9Cnl=C3=BC?= Date: Fri, 20 Jan 2023 11:33:41 +0300 Subject: [PATCH 24/29] docs: add live preview --- .../core/hooks/data/useInfiniteList.md | 79 ++++++++++++++++++- 1 file changed, 75 insertions(+), 4 deletions(-) diff --git a/documentation/docs/api-reference/core/hooks/data/useInfiniteList.md b/documentation/docs/api-reference/core/hooks/data/useInfiniteList.md index f9dec481f03e..9668fddfb5ba 100644 --- a/documentation/docs/api-reference/core/hooks/data/useInfiniteList.md +++ b/documentation/docs/api-reference/core/hooks/data/useInfiniteList.md @@ -9,7 +9,78 @@ In some APIs, the `cursor-pagination` method is used for its various advantages. It uses the `getList` method as the query function from the [`dataProvider`](/docs/api-reference/core/providers/data-provider.md) which is passed to ``. -*TODO: Add LivePreview* +```tsx live url=http://localhost:3000/categories previewHeight=420px +import React from "react"; +import { Refine } from "@pankod/refine-core"; + +setInitialRoutes(["/posts"]); +// visible-block-start +import React from "react"; +import { useInfiniteList } from "@pankod/refine-core"; + +const PostList = () => { + const { + data, + error, + hasNextPage, + isLoading, + fetchNextPage, + isFetchingNextPage, + } = useInfiniteList({ + resource: "categories", + config: { + pagination: { + pageSize: 4 + } + } + }); + + if (isLoading) { + return

    Loading

    ; + } + if (error) { + return

    Something went wrong

    ; + } + + return ( +
    +
      + {data?.pages.map((page) => + page.data.map(({ id, title }) => ( +
    • + {id}.{title} +
    • + )), + )} +
    + + +
    + ); +} +// visible-block-end + +setRefineProps({ + // Layout: (props: LayoutProps) => , + resources: [ + { + name: "posts", + list: PostList, + }, + ], +}); + +render(); +``` ## Usage @@ -46,7 +117,7 @@ Consumes data from data provider `useInfiniteList` with `getList` method. First getList: async ({ resource, pagination }) => { const { current } = pagination; const { data } = await axios.get( - `https://api.fake-rest.refine.dev/${resource}?cursor=${cursor || 0}`, + `https://api.fake-rest.refine.dev/${resource}?cursor=${current || 0}`, ); return { @@ -66,7 +137,7 @@ After this process, we have successfully retrieved the first page data. Let's fi getList: async ({ resource, pagination }) => { const { current } = pagination; const { data } = await axios.get( - `https://api.fake-rest.refine.dev/${resource}?cursor=${cursor || 0}`, + `https://api.fake-rest.refine.dev/${resource}?cursor=${current || 0}`, ); return { @@ -292,7 +363,7 @@ const postListQueryResult = useInfiniteList({
    :::tip -`useInfiniteList` can also accept all `useInfiniteQuery` options as a third parameter. +`useInfiniteList` can also accept all `useInfiniteQuery` options as a parameter. [Refer to react-query docs for further information. →](https://react-query.tanstack.com/reference/useInfiniteQuery) - For example, to disable query from running automatically you can set `enabled` to `false`. From 5403f0635ec20fb7a2abd42da14f7348eb92bb14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C4=B1ld=C4=B1ray=20=C3=9Cnl=C3=BC?= Date: Fri, 20 Jan 2023 13:28:20 +0300 Subject: [PATCH 25/29] docs: add faq section --- .../core/hooks/data/useInfiniteList.md | 371 ++++++++---------- 1 file changed, 158 insertions(+), 213 deletions(-) diff --git a/documentation/docs/api-reference/core/hooks/data/useInfiniteList.md b/documentation/docs/api-reference/core/hooks/data/useInfiniteList.md index 9668fddfb5ba..e3b96a3e1b6e 100644 --- a/documentation/docs/api-reference/core/hooks/data/useInfiniteList.md +++ b/documentation/docs/api-reference/core/hooks/data/useInfiniteList.md @@ -5,11 +5,11 @@ siderbar_label: useInfiniteList description: useInfiniteList data hook from refine is a modified version of react-query's useInfiniteQuery for retrieving items from a resource with pagination, search, sort, and filter configurations. --- -In some APIs, the `cursor-pagination` method is used for its various advantages. With **refine**, this type of API's can be retrieved with the [`useInfiniteQuery`](https://tanstack.com/query/v4/docs/react/reference/useInfiniteQuery) data hook, pagination, filtering and sorting can be done. Ideal for fetching data of the next page and user with a button (or scroll listener) where the total number of records is unknown. +`useInfiniteList` is a modified version of `react-query`'s [`useInfiniteQuery`](https://react-query.tanstack.com/guides/useInfiniteQuery) used for retrieving items from a `resource` with pagination, sort, and filter configurations. It is ideal for lists where the total number of records is unknown and the user loads the next pages with a button. It uses the `getList` method as the query function from the [`dataProvider`](/docs/api-reference/core/providers/data-provider.md) which is passed to ``. -```tsx live url=http://localhost:3000/categories previewHeight=420px +```tsx live url=http://localhost:3000/categories previewHeight=420px hideCode import React from "react"; import { Refine } from "@pankod/refine-core"; @@ -54,16 +54,16 @@ const PostList = () => { )} - + { + hasNextPage && ( + + ) + } ); } @@ -84,11 +84,11 @@ render(); ## Usage -Let's consider the following API as an example. Paging is done with the `cursor` parameter. Returns in cursor object whether there are next and previous pages. +Let's say that we have a resource named `posts` -```ts title="https://api.fake-rest.refine.dev/posts?cursor=0" +```ts title="https://api.fake-rest.refine.dev/posts" { - posts: [ + [ { id: 1, title: "E-business", @@ -98,148 +98,81 @@ Let's consider the following API as an example. Paging is done with the `cursor` id: 2, title: "Virtual Invoice Avon", status: "published", - } - ], - cursor: { - next: 2, - prev: undefined, - }, + }, + { + id: 3, + title: "Powerful Crypto", + status: "rejected", + }, + ]; } ``` -APIs of this type return the next page by sending a unique value of the last displayed row to the paging. The `id` value is used here. For the next page, the request should be request as `https://api.fake-rest.refine.dev/posts?cursor=2`. +First of all, we will use `useInfiniteList` without passing any query configurations. -### Preparing the data provider +```tsx +import { useInfiniteList } from "@pankod/refine-core"; -Consumes data from data provider `useInfiniteList` with `getList` method. First of all, we need to make the this method in the data provider convenient for this API. The `cursor` data is kept in `pagination` and should be set to `0` by default. +type IPost = { + id: number; + title: string; + status: "rejected" | "published" | "draft"; +}; -```ts -getList: async ({ resource, pagination }) => { - const { current } = pagination; - const { data } = await axios.get( - `https://api.fake-rest.refine.dev/${resource}?cursor=${current || 0}`, - ); - - return { - data: data[resource], - total: 0, - }; -}, +const postInfiniteListResult = useInfiniteList({ resource: "posts" }); ``` -:::tip -As the `total` data is only needed in the `offset-pagination` method, define it as `0` here. -::: +```json title="postInfiniteListResult" +{ + "status": "success", + "data": { + "pages": [ + { + "data": [ + { + "id": 1, + "title": "E-business", + "status": "draft" + }, + { + "id": 2, + "title": "Virtual Invoice Avon", + "status": "published" + } + ], + "total": 1370 + } + ] + }, + "hasNextPage": true, + "hasPreviousPage": false, + "isFetchingNextPage": false, + "isFetchingPreviousPage": false + ... +} +``` -After this process, we have successfully retrieved the first page data. Let's fill the `cursor` object for the next page. +Although we didn't pass any sort order configurations to `useInfiniteList`, data comes in descending order according to `id` since `getList` has default values for sort order: ```ts -getList: async ({ resource, pagination }) => { - const { current } = pagination; - const { data } = await axios.get( - `https://api.fake-rest.refine.dev/${resource}?cursor=${current || 0}`, - ); - - return { - data: data[resource], - total: 0, - // highlight-start - cursor: { - next: data.cursor.next, - prev: data.cursor.prev, - }, - // highlight-end - }; -}, +{ + sort: [{ order: "desc", field: "id" }]; +} ``` -### Handling the next pages - -If you returned a `cursor` object as described above, **refine** will do the pagination automatically. All pages can be accessed in the `pages` array as below. - -```tsx -import { useInfiniteList } from "@pankod/refine-core"; - -const { - data, - error, - hasNextPage, - isLoading, - fetchNextPage, - isFetchingNextPage, -} = useInfiniteList({ - resource: "posts", -}); +:::caution +`getList` also has default values for pagination: -if (isLoading) { - return

    Loading

    ; -} -if (error) { - return

    Something went wrong

    ; +```ts +{ + pagination: { current: 1, pageSize: 10 } } - -return ( -
    -
      - {data?.pages.map((page) => - page.data.map(({ id, title, createdAt }) => ( -
    • - {createdAt} -
      - {title} -
    • - )), - )} -
    - - { - hasNextPage && ( - - ) - } -
    -); ``` -By default, `refine` expects you to return the `cursor` object, but is not required. This is because some APIs don't work that way. To fix this problem you need to override the `getNextPageParam` method and return the next `cursor`. - -```tsx -import { useInfiniteList } from "@pankod/refine-core"; - -const { - data, - error, - hasNextPage, - isLoading, - fetchNextPage, - isFetchingNextPage, -} = useInfiniteList({ - resource: "posts", - // highlight-start - queryOptions: { - getNextPageParam: (lastPage, allPages) => { - // return the last post's id - const { data } = lastPage; - const lastPost = data[data.length - 1]; - return lastPost.id; - }, - }, - // highlight-end -}); -``` -:::tip -When you override this method, you can access the `lastPage` and `allPages`. ::: - -### Support for `offset-pagination` - -The pagination method prepared for the [`useList`](/docs/api-reference/core/hooks/data/useList.md) hook can also be used here. - -*TODO: More Information* +:::caution +If you want to create your own `getList` method, it will automatically implement default query configurations since `useInfiniteList` can work with no configuration parameters. +::: ### Query Configuration @@ -260,10 +193,6 @@ const postListQueryResult = useInfiniteList({ }); ``` -> Listing will start from page 3 showing 8 records. - -
    - #### `sort` Allows us to sort records by the speficified order and field. @@ -279,36 +208,13 @@ const postListQueryResult = useInfiniteList({ }); ``` -```ts title="postListQueryResult.data" -{ - data: [ - { - id: 1, - title: "E-business", - status: "draft" - }, - { - id: 3, - title: "Powerful Crypto", - status: "rejected" - }, - { - id: 2, - title: "Virtual Invoice Avon", - status: "published" - }, - ], -} -``` - -> Listing starts from ascending alphabetical order on the `title` field. - -
    - #### `filters` Allows us to filter queries using refine's filter operators. It is configured via `field`, `operator` and `value` properites. +[Refer to supported operators. →](/docs/api-reference/core/interfaceReferences/#crudfilters) + + ```ts import { useInfiniteList } from "@pankod/refine-core"; @@ -326,45 +232,8 @@ const postListQueryResult = useInfiniteList({ }); ``` -```ts title="postListQueryResult.data" -{ - data: [ - { - id: 3, - title: "Powerful Crypto", - status: "rejected" - }, - ], -} -``` - -> Only lists records whose `status` equals to "rejected". - -
    - -**Supported operators** - -| Filter | Description | -| ------------ | ------------------------------- | -| `eq` | Equal | -| `ne` | Not equal | -| `lt` | Less than | -| `gt` | Greater than | -| `lte` | Less than or equal to | -| `gte` | Greater than or equal to | -| `in` | Included in an array | -| `nin` | Not included in an array | -| `contains` | Contains | -| `ncontains` | Doesn't contain | -| `containss` | Contains, case sensitive | -| `ncontainss` | Doesn't contain, case sensitive | -| `null` | Is null or not null | - -
    - :::tip -`useInfiniteList` can also accept all `useInfiniteQuery` options as a parameter. -[Refer to react-query docs for further information. →](https://react-query.tanstack.com/reference/useInfiniteQuery) +`useInfiniteList` returns the result of `react-query`'s `useInfiniteQuery` which includes many properties such as `fetchNextPage`, `hasNextPage` and `isFetchingNextPage`. - For example, to disable query from running automatically you can set `enabled` to `false`. @@ -385,15 +254,10 @@ const postListQueryResult = useInfiniteList({ }); ``` -::: - -
    - -:::tip -`useInfiniteList` returns the result of `react-query`'s `useInfiniteQuery` which includes many properties such as `fetchNextPage`, `hasNextPage` and `isFetchingNextPage`. [Refer to react-query docs for further information. →](https://react-query.tanstack.com/reference/useInfiniteQuery) ::: + ## API ### Properties @@ -436,3 +300,84 @@ interface UseListConfig { | Description | Type | | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Result of the `react-query`'s `useInfiniteQuery` | [`InfiniteQueryObserverResult<{`
    ` data: TData[];`
    ` total: number; },`
    ` TError>`](https://react-query.tanstack.com/reference/useInfiniteQuery) | + +## FAQ +### How to use cursor based pagination? + +Some APIs use the `cursor-pagination` method for its benefits. This method uses a `cursor` object to determine the next set of data. The cursor can be a number or a string and is passed to the API as a query parameter. + +**Preparing the data provider:** + +Consumes data from data provider `useInfiniteList` with `getList` method. First of all, we need to make the this method in the data provider convenient for this API. The `cursor` data is kept in `pagination` and should be set to `0` by default. + +```ts +getList: async ({ resource, pagination }) => { + const { current } = pagination; + const { data } = await axios.get( + `https://api.fake-rest.refine.dev/${resource}?cursor=${current || 0}`, + ); + + return { + data: data[resource], + total: 0, + }; +}, +``` + +:::tip +As the `total` data is only needed in the `offset-pagination` method, define it as `0` here. +::: + +After this process, we have successfully retrieved the first page data. Let's fill the `cursor` object for the next page. + +```ts +getList: async ({ resource, pagination }) => { + const { current } = pagination; + const { data } = await axios.get( + `https://api.fake-rest.refine.dev/${resource}?cursor=${current || 0}`, + ); + + return { + data: data[resource], + total: 0, + // highlight-start + cursor: { + next: data.cursor.next, + prev: data.cursor.prev, + }, + // highlight-end + }; +}, +``` + +### How to override the `getNextPageParam` method? + +By default, `refine` expects you to return the `cursor` object, but is not required. This is because some APIs don't work that way. To fix this problem you need to override the `getNextPageParam` method and return the next `cursor`. + +```tsx +import { useInfiniteList } from "@pankod/refine-core"; + +const { + data, + error, + hasNextPage, + isLoading, + fetchNextPage, + isFetchingNextPage, +} = useInfiniteList({ + resource: "posts", + // highlight-start + queryOptions: { + getNextPageParam: (lastPage, allPages) => { + // return the last post's id + const { data } = lastPage; + const lastPost = data[data.length - 1]; + return lastPost.id; + }, + }, + // highlight-end +}); +``` +:::tip +When you override this method, you can access the `lastPage` and `allPages`. +::: \ No newline at end of file From 527c0f96a7b5d3044498a2a6bf71e1d9a5b55455 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C4=B1ld=C4=B1ray=20=C3=9Cnl=C3=BC?= Date: Fri, 20 Jan 2023 14:29:40 +0300 Subject: [PATCH 26/29] docs: remove default sorting --- .../docs/api-reference/core/hooks/data/useInfiniteList.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/documentation/docs/api-reference/core/hooks/data/useInfiniteList.md b/documentation/docs/api-reference/core/hooks/data/useInfiniteList.md index e3b96a3e1b6e..c99b54faf1c1 100644 --- a/documentation/docs/api-reference/core/hooks/data/useInfiniteList.md +++ b/documentation/docs/api-reference/core/hooks/data/useInfiniteList.md @@ -152,14 +152,6 @@ const postInfiniteListResult = useInfiniteList({ resource: "posts" }); } ``` -Although we didn't pass any sort order configurations to `useInfiniteList`, data comes in descending order according to `id` since `getList` has default values for sort order: - -```ts -{ - sort: [{ order: "desc", field: "id" }]; -} -``` - :::caution `getList` also has default values for pagination: From c67763ad456ea9c0e41e298a9ba215c12af0b038 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C4=B1ld=C4=B1ray=20=C3=9Cnl=C3=BC?= Date: Fri, 20 Jan 2023 15:33:58 +0300 Subject: [PATCH 27/29] chore: cursor type on IDataContextProvider --- packages/core/src/contexts/data/IDataContext.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/core/src/contexts/data/IDataContext.ts b/packages/core/src/contexts/data/IDataContext.ts index 0336e6ad8b85..1553fdd3df30 100644 --- a/packages/core/src/contexts/data/IDataContext.ts +++ b/packages/core/src/contexts/data/IDataContext.ts @@ -131,7 +131,6 @@ export interface IDataContextProvider { filters?: CrudFilters; metaData?: MetaDataQuery; dataProviderName?: string; - cursor?: unknown; }) => Promise>; getMany?: (params: { resource: string; From 56148c09062e82fb8e789a033b4196ce4c09dca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C4=B1ld=C4=B1ray=20=C3=9Cnl=C3=BC?= Date: Mon, 23 Jan 2023 12:35:15 +0300 Subject: [PATCH 28/29] docs: grammer fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Salih Ă–zdemir --- .../docs/api-reference/core/hooks/data/useInfiniteList.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/api-reference/core/hooks/data/useInfiniteList.md b/documentation/docs/api-reference/core/hooks/data/useInfiniteList.md index c99b54faf1c1..ab4c4375cec8 100644 --- a/documentation/docs/api-reference/core/hooks/data/useInfiniteList.md +++ b/documentation/docs/api-reference/core/hooks/data/useInfiniteList.md @@ -84,7 +84,7 @@ render(); ## Usage -Let's say that we have a resource named `posts` +Let's assume that we have a `posts` resource with the following data: ```ts title="https://api.fake-rest.refine.dev/posts" { From cf0fbaeb84a1f0df4d77f8bab1a7d9119495a303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C4=B1ld=C4=B1ray=20=C3=9Cnl=C3=BC?= Date: Mon, 23 Jan 2023 15:03:41 +0300 Subject: [PATCH 29/29] docs: pr review --- .../docs/api-reference/core/hooks/data/useInfiniteList.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/documentation/docs/api-reference/core/hooks/data/useInfiniteList.md b/documentation/docs/api-reference/core/hooks/data/useInfiniteList.md index ab4c4375cec8..453682db3a9f 100644 --- a/documentation/docs/api-reference/core/hooks/data/useInfiniteList.md +++ b/documentation/docs/api-reference/core/hooks/data/useInfiniteList.md @@ -152,6 +152,8 @@ const postInfiniteListResult = useInfiniteList({ resource: "posts" }); } ``` +If your API returns the result like above, you can use useInfiniteList without any configuration. + :::caution `getList` also has default values for pagination: