浏览代码

Use react-query for server state managment (#2133)

* Use react-query for server state managment

* Refactor using of react query
Oleg Shur 3 年之前
父节点
当前提交
70656b7fc0
共有 36 个文件被更改,包括 437 次插入608 次删除
  1. 173 4
      kafka-ui-react-app/package-lock.json
  2. 1 0
      kafka-ui-react-app/package.json
  3. 14 40
      kafka-ui-react-app/src/components/Brokers/Broker/Broker.tsx
  4. 49 72
      kafka-ui-react-app/src/components/Brokers/Broker/__test__/Broker.spec.tsx
  5. 1 1
      kafka-ui-react-app/src/components/Brokers/Brokers.tsx
  6. 9 19
      kafka-ui-react-app/src/components/Brokers/BrokersList/BrokersList.tsx
  7. 8 8
      kafka-ui-react-app/src/components/Brokers/BrokersList/__test__/BrokersList.spec.tsx
  8. 3 4
      kafka-ui-react-app/src/components/Brokers/__test__/Brokers.spec.tsx
  9. 4 4
      kafka-ui-react-app/src/components/Brokers/__test__/fixtures.ts
  10. 59 55
      kafka-ui-react-app/src/components/Cluster/Cluster.tsx
  11. 5 3
      kafka-ui-react-app/src/components/Cluster/__tests__/Cluster.spec.tsx
  12. 1 1
      kafka-ui-react-app/src/components/Schemas/Details/Details.tsx
  13. 1 1
      kafka-ui-react-app/src/components/Schemas/Edit/Edit.tsx
  14. 2 4
      kafka-ui-react-app/src/components/Schemas/List/GlobalSchemaSelector/GlobalSchemaSelector.tsx
  15. 2 4
      kafka-ui-react-app/src/components/Schemas/New/New.tsx
  16. 1 1
      kafka-ui-react-app/src/components/Topics/Topic/SendMessage/SendMessage.tsx
  17. 6 1
      kafka-ui-react-app/src/index.tsx
  18. 23 0
      kafka-ui-react-app/src/lib/api.ts
  19. 11 0
      kafka-ui-react-app/src/lib/hooks/useBrokers.tsx
  20. 18 0
      kafka-ui-react-app/src/lib/hooks/useBrokersLogDirs.tsx
  21. 11 0
      kafka-ui-react-app/src/lib/hooks/useClusterStats.tsx
  22. 0 25
      kafka-ui-react-app/src/lib/hooks/useInterval.ts
  23. 10 3
      kafka-ui-react-app/src/lib/testHelpers.tsx
  24. 0 113
      kafka-ui-react-app/src/redux/reducers/brokers/__test__/reducer.spec.ts
  25. 0 83
      kafka-ui-react-app/src/redux/reducers/brokers/__test__/selectors.spec.ts
  26. 0 52
      kafka-ui-react-app/src/redux/reducers/brokers/brokersSlice.ts
  27. 0 43
      kafka-ui-react-app/src/redux/reducers/brokers/selectors.ts
  28. 3 11
      kafka-ui-react-app/src/redux/reducers/clusters/clustersSlice.ts
  29. 1 6
      kafka-ui-react-app/src/redux/reducers/connect/connectSlice.ts
  30. 6 10
      kafka-ui-react-app/src/redux/reducers/consumerGroups/consumerGroupsSlice.ts
  31. 0 2
      kafka-ui-react-app/src/redux/reducers/index.ts
  32. 2 10
      kafka-ui-react-app/src/redux/reducers/ksqlDb/ksqlDbSlice.ts
  33. 1 5
      kafka-ui-react-app/src/redux/reducers/loader/loaderSlice.ts
  34. 2 6
      kafka-ui-react-app/src/redux/reducers/schemas/schemasSlice.ts
  35. 2 5
      kafka-ui-react-app/src/redux/reducers/topicMessages/topicMessagesSlice.ts
  36. 8 12
      kafka-ui-react-app/src/redux/reducers/topics/topicsSlice.ts

+ 173 - 4
kafka-ui-react-app/package-lock.json

@@ -33,6 +33,7 @@
         "react-dom": "^18.1.0",
         "react-hook-form": "7.6.9",
         "react-multi-select-component": "^4.0.6",
+        "react-query": "^3.39.1",
         "react-redux": "^7.2.6",
         "react-router-dom": "^6.3.0",
         "redux": "^4.1.1",
@@ -10512,6 +10513,14 @@
         "node": ">= 8.0.0"
       }
     },
+    "node_modules/big-integer": {
+      "version": "1.6.51",
+      "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz",
+      "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==",
+      "engines": {
+        "node": ">=0.6"
+      }
+    },
     "node_modules/big.js": {
       "version": "5.2.2",
       "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
@@ -10617,6 +10626,21 @@
         "node": ">=8"
       }
     },
+    "node_modules/broadcast-channel": {
+      "version": "3.7.0",
+      "resolved": "https://registry.npmjs.org/broadcast-channel/-/broadcast-channel-3.7.0.tgz",
+      "integrity": "sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==",
+      "dependencies": {
+        "@babel/runtime": "^7.7.2",
+        "detect-node": "^2.1.0",
+        "js-sha3": "0.8.0",
+        "microseconds": "0.2.0",
+        "nano-time": "1.0.0",
+        "oblivious-set": "1.0.0",
+        "rimraf": "3.0.2",
+        "unload": "2.2.0"
+      }
+    },
     "node_modules/browser-process-hrtime": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz",
@@ -12170,8 +12194,7 @@
     "node_modules/detect-node": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
-      "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
-      "dev": true
+      "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g=="
     },
     "node_modules/detect-port-alt": {
       "version": "1.1.6",
@@ -19028,6 +19051,11 @@
         "url": "https://github.com/chalk/supports-color?sponsor=1"
       }
     },
+    "node_modules/js-sha3": {
+      "version": "0.8.0",
+      "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz",
+      "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q=="
+    },
     "node_modules/js-tokens": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -19699,6 +19727,15 @@
         "tmpl": "1.0.x"
       }
     },
+    "node_modules/match-sorter": {
+      "version": "6.3.1",
+      "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.1.tgz",
+      "integrity": "sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==",
+      "dependencies": {
+        "@babel/runtime": "^7.12.5",
+        "remove-accents": "0.4.2"
+      }
+    },
     "node_modules/mdn-data": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz",
@@ -19769,6 +19806,11 @@
         "node": ">=8.6"
       }
     },
+    "node_modules/microseconds": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/microseconds/-/microseconds-0.2.0.tgz",
+      "integrity": "sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA=="
+    },
     "node_modules/mime": {
       "version": "1.6.0",
       "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
@@ -19928,6 +19970,14 @@
       "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
       "dev": true
     },
+    "node_modules/nano-time": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/nano-time/-/nano-time-1.0.0.tgz",
+      "integrity": "sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==",
+      "dependencies": {
+        "big-integer": "^1.6.16"
+      }
+    },
     "node_modules/nanoclone": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz",
@@ -21380,6 +21430,11 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/oblivious-set": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/oblivious-set/-/oblivious-set-1.0.0.tgz",
+      "integrity": "sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw=="
+    },
     "node_modules/obuf": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
@@ -24123,6 +24178,31 @@
         "react": "^16.8.0 || ^17"
       }
     },
+    "node_modules/react-query": {
+      "version": "3.39.1",
+      "resolved": "https://registry.npmjs.org/react-query/-/react-query-3.39.1.tgz",
+      "integrity": "sha512-qYKT1bavdDiQZbngWZyPotlBVzcBjDYEJg5RQLBa++5Ix5jjfbEYJmHSZRZD+USVHUSvl/ey9Hu+QfF1QAK80A==",
+      "dependencies": {
+        "@babel/runtime": "^7.5.5",
+        "broadcast-channel": "^3.4.1",
+        "match-sorter": "^6.0.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/tannerlinsley"
+      },
+      "peerDependencies": {
+        "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+      },
+      "peerDependenciesMeta": {
+        "react-dom": {
+          "optional": true
+        },
+        "react-native": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/react-redux": {
       "version": "7.2.6",
       "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.6.tgz",
@@ -26803,6 +26883,11 @@
         "node": ">= 0.10"
       }
     },
+    "node_modules/remove-accents": {
+      "version": "0.4.2",
+      "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.2.tgz",
+      "integrity": "sha1-CkPTqq4egNuRngeuJUsoXZ4ce7U="
+    },
     "node_modules/renderkid": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz",
@@ -28980,6 +29065,15 @@
         "node": ">= 10.0.0"
       }
     },
+    "node_modules/unload": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/unload/-/unload-2.2.0.tgz",
+      "integrity": "sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==",
+      "dependencies": {
+        "@babel/runtime": "^7.6.2",
+        "detect-node": "^2.0.4"
+      }
+    },
     "node_modules/unpipe": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
@@ -37665,6 +37759,11 @@
         "tryer": "^1.0.1"
       }
     },
+    "big-integer": {
+      "version": "1.6.51",
+      "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz",
+      "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg=="
+    },
     "big.js": {
       "version": "5.2.2",
       "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
@@ -37756,6 +37855,21 @@
         "fill-range": "^7.0.1"
       }
     },
+    "broadcast-channel": {
+      "version": "3.7.0",
+      "resolved": "https://registry.npmjs.org/broadcast-channel/-/broadcast-channel-3.7.0.tgz",
+      "integrity": "sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==",
+      "requires": {
+        "@babel/runtime": "^7.7.2",
+        "detect-node": "^2.1.0",
+        "js-sha3": "0.8.0",
+        "microseconds": "0.2.0",
+        "nano-time": "1.0.0",
+        "oblivious-set": "1.0.0",
+        "rimraf": "3.0.2",
+        "unload": "2.2.0"
+      }
+    },
     "browser-process-hrtime": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz",
@@ -38900,8 +39014,7 @@
     "detect-node": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
-      "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
-      "dev": true
+      "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g=="
     },
     "detect-port-alt": {
       "version": "1.1.6",
@@ -44027,6 +44140,11 @@
         }
       }
     },
+    "js-sha3": {
+      "version": "0.8.0",
+      "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz",
+      "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q=="
+    },
     "js-tokens": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -44554,6 +44672,15 @@
         "tmpl": "1.0.x"
       }
     },
+    "match-sorter": {
+      "version": "6.3.1",
+      "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.1.tgz",
+      "integrity": "sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==",
+      "requires": {
+        "@babel/runtime": "^7.12.5",
+        "remove-accents": "0.4.2"
+      }
+    },
     "mdn-data": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz",
@@ -44609,6 +44736,11 @@
         "picomatch": "^2.2.3"
       }
     },
+    "microseconds": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/microseconds/-/microseconds-0.2.0.tgz",
+      "integrity": "sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA=="
+    },
     "mime": {
       "version": "1.6.0",
       "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
@@ -44723,6 +44855,14 @@
       "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
       "dev": true
     },
+    "nano-time": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/nano-time/-/nano-time-1.0.0.tgz",
+      "integrity": "sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==",
+      "requires": {
+        "big-integer": "^1.6.16"
+      }
+    },
     "nanoclone": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz",
@@ -45756,6 +45896,11 @@
         }
       }
     },
+    "oblivious-set": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/oblivious-set/-/oblivious-set-1.0.0.tgz",
+      "integrity": "sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw=="
+    },
     "obuf": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
@@ -47729,6 +47874,16 @@
         "warning": "^4.0.2"
       }
     },
+    "react-query": {
+      "version": "3.39.1",
+      "resolved": "https://registry.npmjs.org/react-query/-/react-query-3.39.1.tgz",
+      "integrity": "sha512-qYKT1bavdDiQZbngWZyPotlBVzcBjDYEJg5RQLBa++5Ix5jjfbEYJmHSZRZD+USVHUSvl/ey9Hu+QfF1QAK80A==",
+      "requires": {
+        "@babel/runtime": "^7.5.5",
+        "broadcast-channel": "^3.4.1",
+        "match-sorter": "^6.0.2"
+      }
+    },
     "react-redux": {
       "version": "7.2.6",
       "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.6.tgz",
@@ -49725,6 +49880,11 @@
       "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=",
       "dev": true
     },
+    "remove-accents": {
+      "version": "0.4.2",
+      "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.2.tgz",
+      "integrity": "sha1-CkPTqq4egNuRngeuJUsoXZ4ce7U="
+    },
     "renderkid": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz",
@@ -51313,6 +51473,15 @@
       "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
       "dev": true
     },
+    "unload": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/unload/-/unload-2.2.0.tgz",
+      "integrity": "sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==",
+      "requires": {
+        "@babel/runtime": "^7.6.2",
+        "detect-node": "^2.0.4"
+      }
+    },
     "unpipe": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",

+ 1 - 0
kafka-ui-react-app/package.json

@@ -29,6 +29,7 @@
     "react-dom": "^18.1.0",
     "react-hook-form": "7.6.9",
     "react-multi-select-component": "^4.0.6",
+    "react-query": "^3.39.1",
     "react-redux": "^7.2.6",
     "react-router-dom": "^6.3.0",
     "redux": "^4.1.1",

+ 14 - 40
kafka-ui-react-app/src/components/Brokers/Broker/Broker.tsx

@@ -1,15 +1,6 @@
-import React, { useState } from 'react';
-import useInterval from 'lib/hooks/useInterval';
+import React from 'react';
 import PageHeading from 'components/common/PageHeading/PageHeading';
-import { BrokersApi, Configuration } from 'generated-sources';
-import { BASE_PARAMS } from 'lib/constants';
 import * as Metrics from 'components/common/Metrics';
-import { useAppDispatch, useAppSelector } from 'lib/hooks/redux';
-import {
-  fetchBrokers,
-  fetchClusterStats,
-  selectStats,
-} from 'redux/reducers/brokers/brokersSlice';
 import BytesFormatted from 'components/common/BytesFormatted/BytesFormatted';
 import useAppParams from 'lib/hooks/useAppParams';
 import { translateLogdir } from 'components/Brokers/utils/translateLogdir';
@@ -17,9 +8,9 @@ import { SmartTable } from 'components/common/SmartTable/SmartTable';
 import { TableColumn } from 'components/common/SmartTable/TableColumn';
 import { useTableState } from 'lib/hooks/useTableState';
 import { ClusterBrokerParam } from 'lib/paths';
-
-const apiClientConf = new Configuration(BASE_PARAMS);
-export const brokersApiClient = new BrokersApi(apiClientConf);
+import useClusterStats from 'lib/hooks/useClusterStats';
+import useBrokers from 'lib/hooks/useBrokers';
+import useBrokersLogDirs from 'lib/hooks/useBrokersLogDirs';
 
 export interface BrokerLogdirState {
   name: string;
@@ -29,41 +20,24 @@ export interface BrokerLogdirState {
 }
 
 const Broker: React.FC = () => {
-  const dispatch = useAppDispatch();
   const { clusterName, brokerId } = useAppParams<ClusterBrokerParam>();
 
-  const [logdirs, setLogdirs] = useState<BrokerLogdirState[]>([]);
-  const { diskUsage, items } = useAppSelector(selectStats);
-
-  React.useEffect(() => {
-    brokersApiClient
-      .getAllBrokersLogdirs({
-        clusterName,
-        broker: [Number(brokerId)],
-      })
-      .then((res) => {
-        if (res && res[0]) {
-          setLogdirs([translateLogdir(res[0])]);
-        }
-      });
-    dispatch(fetchClusterStats(clusterName));
-    dispatch(fetchBrokers(clusterName));
-  }, [clusterName, brokerId, dispatch]);
+  const { data: clusterStats } = useClusterStats(clusterName);
+  const { data: brokers } = useBrokers(clusterName);
+  const { data: logDirs } = useBrokersLogDirs(clusterName, Number(brokerId));
 
-  const tableState = useTableState<BrokerLogdirState, string>(logdirs, {
-    idSelector: (logdir) => logdir.name,
+  const preparedRows = logDirs?.map(translateLogdir) || [];
+  const tableState = useTableState<BrokerLogdirState, string>(preparedRows, {
+    idSelector: ({ name }) => name,
     totalPages: 0,
   });
 
-  const brokerItem = items?.find((item) => item.id === Number(brokerId));
-  const brokerDiskUsage = diskUsage?.find(
+  if (!clusterStats) return null;
+
+  const brokerItem = brokers?.find(({ id }) => id === Number(brokerId));
+  const brokerDiskUsage = clusterStats.diskUsage?.find(
     (item) => item.brokerId === Number(brokerId)
   );
-
-  useInterval(() => {
-    fetchClusterStats(clusterName);
-    fetchBrokers(clusterName);
-  }, 5000);
   return (
     <>
       <PageHeading text={`Broker ${brokerId}`} />

+ 49 - 72
kafka-ui-react-app/src/components/Brokers/Broker/__test__/Broker.spec.tsx

@@ -3,88 +3,65 @@ import { render, WithRoute } from 'lib/testHelpers';
 import { screen, waitFor } from '@testing-library/dom';
 import { clusterBrokerPath } from 'lib/paths';
 import fetchMock from 'fetch-mock';
-import { clusterStatsPayloadBroker } from 'redux/reducers/brokers/__test__/fixtures';
 import { act } from '@testing-library/react';
 import Broker from 'components/Brokers/Broker/Broker';
-import { BrokersLogdirs } from 'generated-sources';
+import {
+  clusterStatsPayload,
+  brokerLogDirsPayload,
+  brokersPayload,
+} from 'components/Brokers/__test__/fixtures';
+
+const clusterName = 'local';
+const brokerId = 1;
+const fetchStatsUrl = `/api/clusters/${clusterName}/stats`;
+const fetchBrokersUrl = `/api/clusters/${clusterName}/brokers`;
+const fetchLogDirsUrl = `/api/clusters/${clusterName}/brokers/logdirs`;
 
 describe('Broker Component', () => {
-  afterEach(() => fetchMock.reset());
-
-  const clusterName = 'local';
-  const brokerId = 1;
-
-  const renderComponent = () =>
-    render(
-      <WithRoute path={clusterBrokerPath()}>
-        <Broker />
-      </WithRoute>,
-      {
-        initialEntries: [clusterBrokerPath(clusterName, brokerId)],
-      }
-    );
-
-  describe('Broker', () => {
-    const fetchBrokerMockUrl = `/api/clusters/${clusterName}/brokers/logdirs?broker=${brokerId}`;
-
-    const actRender = async (
-      mockData: BrokersLogdirs[] = clusterStatsPayloadBroker
-    ) => {
-      const fetchBrokerMock = fetchMock.getOnce(fetchBrokerMockUrl, mockData);
-
-      await act(() => {
-        renderComponent();
-      });
-      await waitFor(() => expect(fetchBrokerMock.called()).toBeTruthy());
-    };
-
-    it('renders', async () => {
-      await actRender();
-
-      expect(screen.getByRole('table')).toBeInTheDocument();
-      const rows = screen.getAllByRole('row');
-      expect(rows.length).toEqual(2);
-    });
-
-    it('show warning when broker not found', async () => {
-      await actRender([]);
-
-      expect(
-        screen.getByText('Log dir data not available')
-      ).toBeInTheDocument();
-    });
+  afterEach(() => {
+    fetchMock.reset();
+  });
 
-    it('show broker found', async () => {
-      await actRender();
-      const topicCount = screen.getByText(
-        clusterStatsPayloadBroker[0].topics?.length || 0
+  const renderComponent = async () => {
+    const fetchStatsMock = fetchMock.get(fetchStatsUrl, clusterStatsPayload);
+    const fetchBrokersMock = fetchMock.get(fetchBrokersUrl, brokersPayload);
+    await act(() => {
+      render(
+        <WithRoute path={clusterBrokerPath()}>
+          <Broker />
+        </WithRoute>,
+        {
+          initialEntries: [clusterBrokerPath(clusterName, brokerId)],
+        }
       );
-      const partitionsCount = screen.getByText(
-        clusterStatsPayloadBroker[0].topics?.reduce(
-          (previousValue, currentValue) =>
-            previousValue + (currentValue.partitions?.length || 0),
-          0
-        ) || 0
-      );
-      expect(topicCount).toBeInTheDocument();
-      expect(partitionsCount).toBeInTheDocument();
     });
+    await waitFor(() => expect(fetchStatsMock.called()).toBeTruthy());
+    expect(fetchBrokersMock.called()).toBeTruthy();
+  };
 
-    it('show 0s when broker has not topics', async () => {
-      await actRender([{ ...clusterStatsPayloadBroker[0], topics: undefined }]);
-
-      expect(screen.getAllByText(0).length).toEqual(2);
+  it('shows warning when server returns empty logDirs response', async () => {
+    const fetchLogDirsMock = fetchMock.getOnce(fetchLogDirsUrl, [], {
+      query: { broker: brokerId },
     });
+    await renderComponent();
+    await waitFor(() => expect(fetchLogDirsMock.called()).toBeTruthy());
+    expect(screen.getByText('Log dir data not available')).toBeInTheDocument();
+  });
 
-    it('show - when broker has not name', async () => {
-      await actRender([{ ...clusterStatsPayloadBroker[0], name: undefined }]);
-
-      expect(screen.getByText('-')).toBeInTheDocument();
-    });
+  it('shows broker found', async () => {
+    const fetchLogDirsMock = fetchMock.getOnce(
+      fetchLogDirsUrl,
+      brokerLogDirsPayload,
+      {
+        query: { broker: brokerId },
+      }
+    );
 
-    it('show - when broker has not error', async () => {
-      await actRender([{ ...clusterStatsPayloadBroker[0], error: undefined }]);
-      expect(screen.getByText('-')).toBeInTheDocument();
-    });
+    await renderComponent();
+    await waitFor(() => expect(fetchLogDirsMock.called()).toBeTruthy());
+    const topicCount = screen.getByText(3);
+    const partitionsCount = screen.getByText(4);
+    expect(topicCount).toBeInTheDocument();
+    expect(partitionsCount).toBeInTheDocument();
   });
 });

+ 1 - 1
kafka-ui-react-app/src/components/Brokers/Brokers.tsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import { Route, Routes } from 'react-router-dom';
 import { getNonExactPath, RouteParams } from 'lib/paths';
-import BrokersList from 'components/Brokers/List/BrokersList';
+import BrokersList from 'components/Brokers/BrokersList/BrokersList';
 import Broker from 'components/Brokers/Broker/Broker';
 import { BreadcrumbRoute } from 'components/common/Breadcrumb/Breadcrumb.route';
 

+ 9 - 19
kafka-ui-react-app/src/components/Brokers/List/BrokersList.tsx → kafka-ui-react-app/src/components/Brokers/BrokersList/BrokersList.tsx

@@ -1,23 +1,22 @@
 import React from 'react';
 import { ClusterName } from 'redux/interfaces';
-import useInterval from 'lib/hooks/useInterval';
 import BytesFormatted from 'components/common/BytesFormatted/BytesFormatted';
 import { NavLink } from 'react-router-dom';
 import TableHeaderCell from 'components/common/table/TableHeaderCell/TableHeaderCell';
 import { Table } from 'components/common/table/Table/Table.styled';
 import PageHeading from 'components/common/PageHeading/PageHeading';
 import * as Metrics from 'components/common/Metrics';
-import { useAppDispatch, useAppSelector } from 'lib/hooks/redux';
-import {
-  fetchBrokers,
-  fetchClusterStats,
-  selectStats,
-} from 'redux/reducers/brokers/brokersSlice';
 import useAppParams from 'lib/hooks/useAppParams';
+import useBrokers from 'lib/hooks/useBrokers';
+import useClusterStats from 'lib/hooks/useClusterStats';
 
 const BrokersList: React.FC = () => {
-  const dispatch = useAppDispatch();
   const { clusterName } = useAppParams<{ clusterName: ClusterName }>();
+  const { data: clusterStats } = useClusterStats(clusterName);
+  const { data: brokers } = useBrokers(clusterName);
+
+  if (!clusterStats) return null;
+
   const {
     brokerCount,
     activeControllers,
@@ -28,21 +27,12 @@ const BrokersList: React.FC = () => {
     underReplicatedPartitionCount,
     diskUsage,
     version,
-    items,
-  } = useAppSelector(selectStats);
+  } = clusterStats;
 
   const replicas = (inSyncReplicasCount ?? 0) + (outOfSyncReplicasCount ?? 0);
   const areAllInSync = inSyncReplicasCount && replicas === inSyncReplicasCount;
   const partitionIsOffline = offlinePartitionCount && offlinePartitionCount > 0;
-  React.useEffect(() => {
-    dispatch(fetchClusterStats(clusterName));
-    dispatch(fetchBrokers(clusterName));
-  }, [clusterName, dispatch]);
 
-  useInterval(() => {
-    fetchClusterStats(clusterName);
-    fetchBrokers(clusterName);
-  }, 5000);
   return (
     <>
       <PageHeading text="Broker" />
@@ -123,7 +113,7 @@ const BrokersList: React.FC = () => {
           {diskUsage &&
             diskUsage.length !== 0 &&
             diskUsage.map(({ brokerId, segmentSize, segmentCount }) => {
-              const brokerItem = items?.find((item) => item.id === brokerId);
+              const brokerItem = brokers?.find(({ id }) => id === brokerId);
               return (
                 <tr key={brokerId}>
                   <td>

+ 8 - 8
kafka-ui-react-app/src/components/Brokers/List/__test__/BrokersList.spec.tsx → kafka-ui-react-app/src/components/Brokers/BrokersList/__test__/BrokersList.spec.tsx

@@ -3,9 +3,12 @@ import { render, WithRoute } from 'lib/testHelpers';
 import { screen, waitFor } from '@testing-library/dom';
 import { clusterBrokersPath } from 'lib/paths';
 import fetchMock from 'fetch-mock';
-import { clusterStatsPayload } from 'redux/reducers/brokers/__test__/fixtures';
 import { act } from '@testing-library/react';
-import BrokersList from 'components/Brokers/List/BrokersList';
+import BrokersList from 'components/Brokers/BrokersList/BrokersList';
+import {
+  brokersPayload,
+  clusterStatsPayload,
+} from 'components/Brokers/__test__/fixtures';
 
 describe('BrokersList Component', () => {
   afterEach(() => fetchMock.reset());
@@ -30,17 +33,14 @@ describe('BrokersList Component', () => {
     const fetchStatsUrl = `/api/clusters/${clusterName}/stats`;
 
     beforeEach(() => {
-      fetchBrokersMock = fetchMock.getOnce(
+      fetchBrokersMock = fetchMock.get(
         `/api/clusters/${clusterName}/brokers`,
-        clusterStatsPayload
+        brokersPayload
       );
     });
 
     it('renders', async () => {
-      const fetchStatsMock = fetchMock.getOnce(
-        fetchStatsUrl,
-        clusterStatsPayload
-      );
+      const fetchStatsMock = fetchMock.get(fetchStatsUrl, clusterStatsPayload);
       await act(() => {
         renderComponent();
       });

+ 3 - 4
kafka-ui-react-app/src/components/Brokers/__tests__/Brokers.spec.tsx → kafka-ui-react-app/src/components/Brokers/__test__/Brokers.spec.tsx

@@ -7,7 +7,7 @@ import Brokers from 'components/Brokers/Brokers';
 const brokersList = 'brokersList';
 const broker = 'brokers';
 
-jest.mock('components/Brokers/List/BrokersList', () => () => (
+jest.mock('components/Brokers/BrokersList/BrokersList', () => () => (
   <div>{brokersList}</div>
 ));
 jest.mock('components/Brokers/Broker/Broker', () => () => <div>{broker}</div>);
@@ -15,11 +15,10 @@ jest.mock('components/Brokers/Broker/Broker', () => () => <div>{broker}</div>);
 describe('Brokers Component', () => {
   const clusterName = 'clusterName';
   const brokerId = '1';
-  const renderComponent = (path?: string) => {
-    return render(<Brokers />, {
+  const renderComponent = (path?: string) =>
+    render(<Brokers />, {
       initialEntries: path ? [path] : undefined,
     });
-  };
 
   it('renders BrokersList', () => {
     renderComponent();

+ 4 - 4
kafka-ui-react-app/src/redux/reducers/brokers/__test__/fixtures.ts → kafka-ui-react-app/src/components/Brokers/__test__/fixtures.ts

@@ -52,7 +52,7 @@ export const updatedBrokersReducerState = {
   version: '2.2.1',
 };
 
-const partitions = {
+const partition = {
   broker: 2,
   offsetLag: 0,
   partition: 2,
@@ -60,17 +60,17 @@ const partitions = {
 };
 const topics = {
   name: '_confluent-ksql-devquery_CTAS_NUMBER_OF_TESTS_59-Aggregate-Aggregate-Materialize-changelog',
-  partitions: [partitions],
+  partitions: [partition],
 };
 
-export const clusterStatsPayloadBroker: BrokersLogdirs[] = [
+export const brokerLogDirsPayload: BrokersLogdirs[] = [
   {
     error: 'NONE',
     name: '/opt/kafka/data-0/logs',
     topics: [
       {
         ...topics,
-        partitions: [partitions, partitions, partitions],
+        partitions: [partition, partition, partition],
       },
       topics,
       {

+ 59 - 55
kafka-ui-react-app/src/components/Cluster/Cluster.tsx

@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { Suspense } from 'react';
 import { useSelector } from 'react-redux';
 import { Routes, Navigate, Route, Outlet } from 'react-router-dom';
 import useAppParams from 'lib/hooks/useAppParams';
@@ -22,12 +22,14 @@ import Topics from 'components/Topics/Topics';
 import Schemas from 'components/Schemas/Schemas';
 import Connect from 'components/Connect/Connect';
 import ClusterContext from 'components/contexts/ClusterContext';
-import Brokers from 'components/Brokers/Brokers';
 import ConsumersGroups from 'components/ConsumerGroups/ConsumerGroups';
 import KsqlDb from 'components/KsqlDb/KsqlDb';
 import Breadcrumb from 'components/common/Breadcrumb/Breadcrumb';
 import { BreadcrumbRoute } from 'components/common/Breadcrumb/Breadcrumb.route';
 import { BreadcrumbProvider } from 'components/common/Breadcrumb/Breadcrumb.provider';
+import PageLoader from 'components/common/PageLoader/PageLoader';
+
+const Brokers = React.lazy(() => import('components/Brokers/Brokers'));
 
 const Cluster: React.FC = () => {
   const { clusterName } = useAppParams<ClusterNameRoute>();
@@ -63,79 +65,81 @@ const Cluster: React.FC = () => {
   return (
     <BreadcrumbProvider>
       <Breadcrumb />
-      <ClusterContext.Provider value={contextValue}>
-        <Routes>
-          <Route
-            path={getNonExactPath(clusterBrokerRelativePath)}
-            element={
-              <BreadcrumbRoute>
-                <Brokers />
-              </BreadcrumbRoute>
-            }
-          />
-          <Route
-            path={getNonExactPath(clusterTopicsRelativePath)}
-            element={
-              <BreadcrumbRoute>
-                <Topics />
-              </BreadcrumbRoute>
-            }
-          />
-          <Route
-            path={getNonExactPath(clusterConsumerGroupsRelativePath)}
-            element={
-              <BreadcrumbRoute>
-                <ConsumersGroups />
-              </BreadcrumbRoute>
-            }
-          />
-          {hasSchemaRegistryConfigured && (
+      <Suspense fallback={<PageLoader />}>
+        <ClusterContext.Provider value={contextValue}>
+          <Routes>
             <Route
-              path={getNonExactPath(clusterSchemasRelativePath)}
+              path={getNonExactPath(clusterBrokerRelativePath)}
               element={
                 <BreadcrumbRoute>
-                  <Schemas />
+                  <Brokers />
                 </BreadcrumbRoute>
               }
             />
-          )}
-          {hasKafkaConnectConfigured && (
             <Route
-              path={getNonExactPath(clusterConnectsRelativePath)}
+              path={getNonExactPath(clusterTopicsRelativePath)}
               element={
                 <BreadcrumbRoute>
-                  <Connect />
+                  <Topics />
                 </BreadcrumbRoute>
               }
             />
-          )}
-          {hasKafkaConnectConfigured && (
             <Route
-              path={getNonExactPath(clusterConnectorsRelativePath)}
+              path={getNonExactPath(clusterConsumerGroupsRelativePath)}
               element={
                 <BreadcrumbRoute>
-                  <Connect />
+                  <ConsumersGroups />
                 </BreadcrumbRoute>
               }
             />
-          )}
-          {hasKsqlDbConfigured && (
+            {hasSchemaRegistryConfigured && (
+              <Route
+                path={getNonExactPath(clusterSchemasRelativePath)}
+                element={
+                  <BreadcrumbRoute>
+                    <Schemas />
+                  </BreadcrumbRoute>
+                }
+              />
+            )}
+            {hasKafkaConnectConfigured && (
+              <Route
+                path={getNonExactPath(clusterConnectsRelativePath)}
+                element={
+                  <BreadcrumbRoute>
+                    <Connect />
+                  </BreadcrumbRoute>
+                }
+              />
+            )}
+            {hasKafkaConnectConfigured && (
+              <Route
+                path={getNonExactPath(clusterConnectorsRelativePath)}
+                element={
+                  <BreadcrumbRoute>
+                    <Connect />
+                  </BreadcrumbRoute>
+                }
+              />
+            )}
+            {hasKsqlDbConfigured && (
+              <Route
+                path={getNonExactPath(clusterKsqlDbRelativePath)}
+                element={
+                  <BreadcrumbRoute>
+                    <KsqlDb />
+                  </BreadcrumbRoute>
+                }
+              />
+            )}
             <Route
-              path={getNonExactPath(clusterKsqlDbRelativePath)}
-              element={
-                <BreadcrumbRoute>
-                  <KsqlDb />
-                </BreadcrumbRoute>
-              }
+              path="/"
+              element={<Navigate to={clusterBrokerRelativePath} replace />}
             />
-          )}
-          <Route
-            path="/"
-            element={<Navigate to={clusterBrokerRelativePath} replace />}
-          />
-        </Routes>
-        <Outlet />
-      </ClusterContext.Provider>
+          </Routes>
+          <Outlet />
+        </ClusterContext.Provider>
+      </Suspense>
     </BreadcrumbProvider>
   );
 };

+ 5 - 3
kafka-ui-react-app/src/components/Cluster/__tests__/Cluster.spec.tsx

@@ -15,6 +15,7 @@ import {
   clusterSchemasPath,
   clusterTopicsPath,
 } from 'lib/paths';
+import { act } from 'react-dom/test-utils';
 
 const CLusterCompText = {
   Topics: 'Topics',
@@ -45,16 +46,17 @@ jest.mock('components/KsqlDb/KsqlDb', () => () => (
 ));
 
 describe('Cluster', () => {
-  const renderComponent = (pathname: string) =>
+  const renderComponent = (pathname: string) => {
     render(
       <WithRoute path={`${clusterPath()}/*`}>
         <Cluster />
       </WithRoute>,
       { initialEntries: [pathname], store }
     );
+  };
 
-  it('renders Brokers', () => {
-    renderComponent(clusterBrokersPath('second'));
+  it('renders Brokers', async () => {
+    await act(() => renderComponent(clusterBrokersPath('second')));
     expect(screen.getByText(CLusterCompText.Brokers)).toBeInTheDocument();
   });
   it('renders Topics', () => {

+ 1 - 1
kafka-ui-react-app/src/components/Schemas/Details/Details.tsx

@@ -21,7 +21,6 @@ import {
   fetchSchemaVersions,
   getAreSchemaLatestFulfilled,
   getAreSchemaVersionsFulfilled,
-  schemasApiClient,
   SCHEMAS_VERSIONS_FETCH_ACTION,
   SCHEMA_LATEST_FETCH_ACTION,
   selectAllSchemaVersions,
@@ -32,6 +31,7 @@ import { getResponse } from 'lib/errorHandling';
 import { resetLoaderById } from 'redux/reducers/loader/loaderSlice';
 import { TableTitle } from 'components/common/table/TableTitle/TableTitle.styled';
 import useAppParams from 'lib/hooks/useAppParams';
+import { schemasApiClient } from 'lib/api';
 
 import LatestVersionItem from './LatestVersion/LatestVersionItem';
 import SchemaVersion from './SchemaVersion/SchemaVersion';

+ 1 - 1
kafka-ui-react-app/src/components/Schemas/Edit/Edit.tsx

@@ -16,7 +16,6 @@ import { useAppDispatch, useAppSelector } from 'lib/hooks/redux';
 import useAppParams from 'lib/hooks/useAppParams';
 import {
   schemaAdded,
-  schemasApiClient,
   fetchLatestSchema,
   getSchemaLatest,
   SCHEMA_LATEST_FETCH_ACTION,
@@ -27,6 +26,7 @@ import { serverErrorAlertAdded } from 'redux/reducers/alerts/alertsSlice';
 import { getResponse } from 'lib/errorHandling';
 import PageLoader from 'components/common/PageLoader/PageLoader';
 import { resetLoaderById } from 'redux/reducers/loader/loaderSlice';
+import { schemasApiClient } from 'lib/api';
 
 import * as S from './Edit.styled';
 

+ 2 - 4
kafka-ui-react-app/src/components/Schemas/List/GlobalSchemaSelector/GlobalSchemaSelector.tsx

@@ -8,11 +8,9 @@ import usePagination from 'lib/hooks/usePagination';
 import useSearch from 'lib/hooks/useSearch';
 import useAppParams from 'lib/hooks/useAppParams';
 import { serverErrorAlertAdded } from 'redux/reducers/alerts/alertsSlice';
-import {
-  fetchSchemas,
-  schemasApiClient,
-} from 'redux/reducers/schemas/schemasSlice';
+import { fetchSchemas } from 'redux/reducers/schemas/schemasSlice';
 import { ClusterNameRoute } from 'lib/paths';
+import { schemasApiClient } from 'lib/api';
 
 import * as S from './GlobalSchemaSelector.styled';
 

+ 2 - 4
kafka-ui-react-app/src/components/Schemas/New/New.tsx

@@ -13,14 +13,12 @@ import Select, { SelectOption } from 'components/common/Select/Select';
 import { Button } from 'components/common/Button/Button';
 import { Textarea } from 'components/common/Textbox/Textarea.styled';
 import PageHeading from 'components/common/PageHeading/PageHeading';
-import {
-  schemaAdded,
-  schemasApiClient,
-} from 'redux/reducers/schemas/schemasSlice';
+import { schemaAdded } from 'redux/reducers/schemas/schemasSlice';
 import { useAppDispatch } from 'lib/hooks/redux';
 import useAppParams from 'lib/hooks/useAppParams';
 import { serverErrorAlertAdded } from 'redux/reducers/alerts/alertsSlice';
 import { getResponse } from 'lib/errorHandling';
+import { schemasApiClient } from 'lib/api';
 
 import * as S from './New.styled';
 

+ 1 - 1
kafka-ui-react-app/src/components/Topics/Topic/SendMessage/SendMessage.tsx

@@ -6,7 +6,6 @@ import {
   RouteParamsClusterTopic,
 } from 'lib/paths';
 import jsf from 'json-schema-faker';
-import { messagesApiClient } from 'redux/reducers/topicMessages/topicMessagesSlice';
 import {
   fetchTopicMessageSchema,
   fetchTopicDetails,
@@ -25,6 +24,7 @@ import {
 import Select, { SelectOption } from 'components/common/Select/Select';
 import useAppParams from 'lib/hooks/useAppParams';
 import Heading from 'components/common/heading/Heading.styled';
+import { messagesApiClient } from 'lib/api';
 
 import validateMessage from './validateMessage';
 import * as S from './SendMessage.styled';

+ 6 - 1
kafka-ui-react-app/src/index.tsx

@@ -2,12 +2,15 @@ import React from 'react';
 import { createRoot } from 'react-dom/client';
 import { BrowserRouter } from 'react-router-dom';
 import { Provider } from 'react-redux';
+import { QueryClient, QueryClientProvider } from 'react-query';
 import * as serviceWorker from 'serviceWorker';
 import App from 'components/App';
 import { store } from 'redux/store';
 import 'theme/index.scss';
 import 'lib/constants';
 
+const queryClient = new QueryClient();
+
 const container =
   document.getElementById('root') || document.createElement('div');
 const root = createRoot(container);
@@ -15,7 +18,9 @@ const root = createRoot(container);
 root.render(
   <Provider store={store}>
     <BrowserRouter basename={window.basePath || '/'}>
-      <App />
+      <QueryClientProvider client={queryClient}>
+        <App />
+      </QueryClientProvider>
     </BrowserRouter>
   </Provider>
 );

+ 23 - 0
kafka-ui-react-app/src/lib/api.ts

@@ -0,0 +1,23 @@
+import {
+  BrokersApi,
+  ClustersApi,
+  Configuration,
+  ConsumerGroupsApi,
+  KafkaConnectApi,
+  KsqlApi,
+  MessagesApi,
+  SchemasApi,
+  TopicsApi,
+} from 'generated-sources';
+import { BASE_PARAMS } from 'lib/constants';
+
+const apiClientConf = new Configuration(BASE_PARAMS);
+
+export const brokersApiClient = new BrokersApi(apiClientConf);
+export const clustersApiClient = new ClustersApi(apiClientConf);
+export const kafkaConnectApiClient = new KafkaConnectApi(apiClientConf);
+export const consumerGroupsApiClient = new ConsumerGroupsApi(apiClientConf);
+export const ksqlDbApiClient = new KsqlApi(apiClientConf);
+export const topicsApiClient = new TopicsApi(apiClientConf);
+export const messagesApiClient = new MessagesApi(apiClientConf);
+export const schemasApiClient = new SchemasApi(apiClientConf);

+ 11 - 0
kafka-ui-react-app/src/lib/hooks/useBrokers.tsx

@@ -0,0 +1,11 @@
+import { brokersApiClient } from 'lib/api';
+import { useQuery } from 'react-query';
+import { ClusterName } from 'redux/interfaces';
+
+export default function useBrokers(clusterName: ClusterName) {
+  return useQuery(
+    ['brokers', clusterName],
+    () => brokersApiClient.getBrokers({ clusterName }),
+    { suspense: true, refetchInterval: 5000 }
+  );
+}

+ 18 - 0
kafka-ui-react-app/src/lib/hooks/useBrokersLogDirs.tsx

@@ -0,0 +1,18 @@
+import { brokersApiClient } from 'lib/api';
+import { useQuery } from 'react-query';
+import { ClusterName } from 'redux/interfaces';
+
+export default function useBrokersLogDirs(
+  clusterName: ClusterName,
+  brokerId: number
+) {
+  return useQuery(
+    ['logDirs', clusterName, brokerId],
+    () =>
+      brokersApiClient.getAllBrokersLogdirs({
+        clusterName,
+        broker: [brokerId],
+      }),
+    { suspense: true, refetchInterval: 5000 }
+  );
+}

+ 11 - 0
kafka-ui-react-app/src/lib/hooks/useClusterStats.tsx

@@ -0,0 +1,11 @@
+import { clustersApiClient } from 'lib/api';
+import { useQuery } from 'react-query';
+import { ClusterName } from 'redux/interfaces';
+
+export default function useClusterStats(clusterName: ClusterName) {
+  return useQuery(
+    ['clusterStats', clusterName],
+    () => clustersApiClient.getClusterStats({ clusterName }),
+    { suspense: true, refetchInterval: 5000 }
+  );
+}

+ 0 - 25
kafka-ui-react-app/src/lib/hooks/useInterval.ts

@@ -1,25 +0,0 @@
-import React from 'react';
-
-type Callback = () => void;
-
-const useInterval = (callback: Callback, delay: number) => {
-  const savedCallback = React.useRef<Callback>();
-
-  React.useEffect(() => {
-    savedCallback.current = callback;
-  }, [callback]);
-
-  // eslint-disable-next-line consistent-return
-  React.useEffect(() => {
-    const tick = () => {
-      if (savedCallback.current) savedCallback.current();
-    };
-
-    if (delay !== null) {
-      const id = setInterval(tick, delay);
-      return () => clearInterval(id);
-    }
-  }, [delay]);
-};
-
-export default useInterval;

+ 10 - 3
kafka-ui-react-app/src/lib/testHelpers.tsx

@@ -14,6 +14,7 @@ import { RootState } from 'redux/interfaces';
 import { configureStore } from '@reduxjs/toolkit';
 import rootReducer from 'redux/reducers';
 import mockStoreCreator from 'redux/store/configureStore/mockStoreCreator';
+import { QueryClient, QueryClientProvider } from 'react-query';
 
 interface CustomRenderOptions extends Omit<RenderOptions, 'wrapper'> {
   preloadedState?: Partial<RootState>;
@@ -57,6 +58,10 @@ const customRender = (
     ...renderOptions
   }: CustomRenderOptions = {}
 ) => {
+  // use new QueryClient instance for each test run to avoid issues with cache
+  const queryClient = new QueryClient({
+    defaultOptions: { queries: { retry: false } },
+  });
   // overrides @testing-library/react render.
   const AllTheProviders: React.FC<PropsWithChildren<unknown>> = ({
     children,
@@ -64,9 +69,11 @@ const customRender = (
     return (
       <ThemeProvider theme={theme}>
         <Provider store={store}>
-          <MemoryRouter initialEntries={initialEntries}>
-            {children}
-          </MemoryRouter>
+          <QueryClientProvider client={queryClient}>
+            <MemoryRouter initialEntries={initialEntries}>
+              {children}
+            </MemoryRouter>
+          </QueryClientProvider>
         </Provider>
       </ThemeProvider>
     );

+ 0 - 113
kafka-ui-react-app/src/redux/reducers/brokers/__test__/reducer.spec.ts

@@ -1,113 +0,0 @@
-import fetchMock from 'fetch-mock-jest';
-import reducer, {
-  initialState,
-  fetchBrokers,
-  fetchClusterStats,
-} from 'redux/reducers/brokers/brokersSlice';
-import mockStoreCreator from 'redux/store/configureStore/mockStoreCreator';
-
-import {
-  brokersPayload,
-  clusterStatsPayload,
-  initialBrokersReducerState,
-  updatedBrokersReducerState,
-} from './fixtures';
-
-const store = mockStoreCreator;
-const clusterName = 'test-sluster-name';
-
-describe('Brokers slice', () => {
-  describe('reducer', () => {
-    it('returns the initial state', () => {
-      expect(reducer(undefined, { type: fetchBrokers.pending })).toEqual(
-        initialState
-      );
-    });
-    it('reacts on fetchBrokers.fullfiled and returns payload', () => {
-      expect(
-        reducer(initialState, {
-          type: fetchBrokers.fulfilled,
-          payload: brokersPayload,
-        })
-      ).toEqual({
-        ...initialState,
-        items: brokersPayload,
-      });
-    });
-    it('reacts on fetchClusterStats.fullfiled and returns payload', () => {
-      expect(
-        reducer(initialBrokersReducerState, {
-          type: fetchClusterStats.fulfilled,
-          payload: clusterStatsPayload,
-        })
-      ).toEqual(updatedBrokersReducerState);
-    });
-  });
-
-  describe('thunks', () => {
-    afterEach(() => {
-      fetchMock.restore();
-      store.clearActions();
-    });
-
-    describe('fetchBrokers', () => {
-      it('creates fetchBrokers.fulfilled when broker are fetched', async () => {
-        fetchMock.getOnce(
-          `/api/clusters/${clusterName}/brokers`,
-          brokersPayload
-        );
-        await store.dispatch(fetchBrokers(clusterName));
-        expect(
-          store.getActions().map(({ type, payload }) => ({ type, payload }))
-        ).toEqual([
-          { type: fetchBrokers.pending.type },
-          {
-            type: fetchBrokers.fulfilled.type,
-            payload: brokersPayload,
-          },
-        ]);
-      });
-
-      it('creates fetchBrokers.rejected when fetched clusters', async () => {
-        fetchMock.getOnce(`/api/clusters/${clusterName}/brokers`, 422);
-        await store.dispatch(fetchBrokers(clusterName));
-        expect(
-          store.getActions().map(({ type, payload }) => ({ type, payload }))
-        ).toEqual([
-          { type: fetchBrokers.pending.type },
-          { type: fetchBrokers.rejected.type },
-        ]);
-      });
-    });
-
-    describe('fetchClusterStats', () => {
-      it('creates fetchClusterStats.fulfilled when broker are fetched', async () => {
-        fetchMock.getOnce(
-          `/api/clusters/${clusterName}/stats`,
-          clusterStatsPayload
-        );
-        await store.dispatch(fetchClusterStats(clusterName));
-        expect(
-          store.getActions().map(({ type, payload }) => ({ type, payload }))
-        ).toEqual([
-          { type: fetchClusterStats.pending.type },
-          {
-            type: fetchClusterStats.fulfilled.type,
-            payload: clusterStatsPayload,
-          },
-        ]);
-      });
-
-      it('creates fetchClusterStats.rejected when fetched clusters', async () => {
-        fetchMock.getOnce(`/api/clusters/${clusterName}/stats`, 422);
-        await store.dispatch(fetchClusterStats(clusterName));
-        expect(
-          store.getActions().map(({ type, payload }) => ({ type, payload }))
-        ).toEqual([
-          { type: fetchClusterStats.pending.type },
-          { type: fetchClusterStats.rejected.type },
-        ]);
-      });
-    });
-  });
-});

+ 0 - 83
kafka-ui-react-app/src/redux/reducers/brokers/__test__/selectors.spec.ts

@@ -1,83 +0,0 @@
-import { store } from 'redux/store';
-import * as selectors from 'redux/reducers/brokers/selectors';
-import {
-  fetchBrokers,
-  fetchClusterStats,
-} from 'redux/reducers/brokers/brokersSlice';
-
-import { brokersPayload, updatedBrokersReducerState } from './fixtures';
-
-const { dispatch, getState } = store;
-
-describe('Brokers selectors', () => {
-  describe('Initial State', () => {
-    it('returns broker count', () => {
-      expect(selectors.getBrokerCount(getState())).toEqual(0);
-    });
-    it('returns active controllers', () => {
-      expect(selectors.getActiveControllers(getState())).toEqual(0);
-    });
-    it('returns online partition count', () => {
-      expect(selectors.getOnlinePartitionCount(getState())).toEqual(0);
-    });
-    it('returns offline partition count', () => {
-      expect(selectors.getOfflinePartitionCount(getState())).toEqual(0);
-    });
-    it('returns in sync replicas count', () => {
-      expect(selectors.getInSyncReplicasCount(getState())).toEqual(0);
-    });
-    it('returns out of sync replicas count', () => {
-      expect(selectors.getOutOfSyncReplicasCount(getState())).toEqual(0);
-    });
-    it('returns under replicated partition count', () => {
-      expect(selectors.getUnderReplicatedPartitionCount(getState())).toEqual(0);
-    });
-    it('returns disk usage', () => {
-      expect(selectors.getDiskUsage(getState())).toEqual([]);
-    });
-    it('returns version', () => {
-      expect(selectors.getVersion(getState())).toBeUndefined();
-    });
-  });
-
-  describe('state', () => {
-    beforeAll(() => {
-      dispatch({ type: fetchBrokers.fulfilled.type, payload: brokersPayload });
-      dispatch({
-        type: fetchClusterStats.fulfilled.type,
-        payload: updatedBrokersReducerState,
-      });
-    });
-
-    it('returns broker count', () => {
-      expect(selectors.getBrokerCount(getState())).toEqual(2);
-    });
-    it('returns active controllers', () => {
-      expect(selectors.getActiveControllers(getState())).toEqual(1);
-    });
-    it('returns online partition count', () => {
-      expect(selectors.getOnlinePartitionCount(getState())).toEqual(138);
-    });
-    it('returns offline partition count', () => {
-      expect(selectors.getOfflinePartitionCount(getState())).toEqual(0);
-    });
-    it('returns in sync replicas count', () => {
-      expect(selectors.getInSyncReplicasCount(getState())).toEqual(239);
-    });
-    it('returns out of sync replicas count', () => {
-      expect(selectors.getOutOfSyncReplicasCount(getState())).toEqual(0);
-    });
-    it('returns under replicated partition count', () => {
-      expect(selectors.getUnderReplicatedPartitionCount(getState())).toEqual(0);
-    });
-    it('returns disk usage', () => {
-      expect(selectors.getDiskUsage(getState())).toEqual([
-        { brokerId: 0, segmentSize: 334567, segmentCount: 245 },
-        { brokerId: 1, segmentSize: 12345678, segmentCount: 121 },
-      ]);
-    });
-    it('returns version', () => {
-      expect(selectors.getVersion(getState())).toEqual('2.2.1');
-    });
-  });
-});

+ 0 - 52
kafka-ui-react-app/src/redux/reducers/brokers/brokersSlice.ts

@@ -1,52 +0,0 @@
-import { BrokersApi, ClustersApi, Configuration } from 'generated-sources';
-import { BrokersState, ClusterName, RootState } from 'redux/interfaces';
-import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
-import { BASE_PARAMS } from 'lib/constants';
-
-const apiClientConf = new Configuration(BASE_PARAMS);
-export const brokersApiClient = new BrokersApi(apiClientConf);
-export const clustersApiClient = new ClustersApi(apiClientConf);
-
-export const fetchBrokers = createAsyncThunk(
-  'brokers/fetchBrokers',
-  (clusterName: ClusterName) => brokersApiClient.getBrokers({ clusterName })
-);
-
-export const fetchClusterStats = createAsyncThunk(
-  'brokers/fetchClusterStats',
-  (clusterName: ClusterName) =>
-    clustersApiClient.getClusterStats({ clusterName })
-);
-
-export const initialState: BrokersState = {
-  items: [],
-  brokerCount: 0,
-  activeControllers: 0,
-  onlinePartitionCount: 0,
-  offlinePartitionCount: 0,
-  inSyncReplicasCount: 0,
-  outOfSyncReplicasCount: 0,
-  underReplicatedPartitionCount: 0,
-  diskUsage: [],
-};
-
-export const brokersSlice = createSlice({
-  name: 'brokers',
-  initialState,
-  reducers: {},
-  extraReducers: (builder) => {
-    builder.addCase(fetchBrokers.pending, () => initialState);
-    builder.addCase(fetchBrokers.fulfilled, (state, { payload }) => ({
-      ...state,
-      items: payload,
-    }));
-    builder.addCase(fetchClusterStats.fulfilled, (state, { payload }) => ({
-      ...state,
-      ...payload,
-    }));
-  },
-});
-
-export const selectStats = (state: RootState) => state.brokers;
-
-export default brokersSlice.reducer;

+ 0 - 43
kafka-ui-react-app/src/redux/reducers/brokers/selectors.ts

@@ -1,43 +0,0 @@
-import { createSelector } from '@reduxjs/toolkit';
-import { RootState, BrokersState } from 'redux/interfaces';
-
-const brokersState = ({ brokers }: RootState): BrokersState => brokers;
-
-export const getBrokerCount = createSelector(
-  brokersState,
-  ({ brokerCount }) => brokerCount
-);
-export const getActiveControllers = createSelector(
-  brokersState,
-  ({ activeControllers }) => activeControllers
-);
-export const getOnlinePartitionCount = createSelector(
-  brokersState,
-  ({ onlinePartitionCount }) => onlinePartitionCount
-);
-export const getOfflinePartitionCount = createSelector(
-  brokersState,
-  ({ offlinePartitionCount }) => offlinePartitionCount
-);
-export const getInSyncReplicasCount = createSelector(
-  brokersState,
-  ({ inSyncReplicasCount }) => inSyncReplicasCount
-);
-export const getOutOfSyncReplicasCount = createSelector(
-  brokersState,
-  ({ outOfSyncReplicasCount }) => outOfSyncReplicasCount
-);
-export const getUnderReplicatedPartitionCount = createSelector(
-  brokersState,
-  ({ underReplicatedPartitionCount }) => underReplicatedPartitionCount
-);
-
-export const getDiskUsage = createSelector(
-  brokersState,
-  ({ diskUsage }) => diskUsage
-);
-
-export const getVersion = createSelector(
-  brokersState,
-  ({ version }) => version
-);

+ 3 - 11
kafka-ui-react-app/src/redux/reducers/clusters/clustersSlice.ts

@@ -3,20 +3,12 @@ import {
   createSlice,
   createSelector,
 } from '@reduxjs/toolkit';
-import {
-  ClustersApi,
-  Configuration,
-  Cluster,
-  ServerStatus,
-  ClusterFeaturesEnum,
-} from 'generated-sources';
-import { BASE_PARAMS, AsyncRequestStatus } from 'lib/constants';
+import { Cluster, ServerStatus, ClusterFeaturesEnum } from 'generated-sources';
+import { clustersApiClient } from 'lib/api';
+import { AsyncRequestStatus } from 'lib/constants';
 import { RootState } from 'redux/interfaces';
 import { createFetchingSelector } from 'redux/reducers/loader/selectors';
 
-const apiClientConf = new Configuration(BASE_PARAMS);
-export const clustersApiClient = new ClustersApi(apiClientConf);
-
 export const fetchClusters = createAsyncThunk(
   'clusters/fetchClusters',
   async () => {

+ 1 - 6
kafka-ui-react-app/src/redux/reducers/connect/connectSlice.ts

@@ -1,18 +1,16 @@
 import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
 import {
-  Configuration,
   Connect,
   Connector,
   ConnectorAction,
   ConnectorState,
   ConnectorTaskStatus,
   FullConnectorInfo,
-  KafkaConnectApi,
   NewConnector,
   Task,
   TaskId,
 } from 'generated-sources';
-import { BASE_PARAMS } from 'lib/constants';
+import { kafkaConnectApiClient } from 'lib/api';
 import { getResponse } from 'lib/errorHandling';
 import {
   ClusterName,
@@ -24,9 +22,6 @@ import {
 } from 'redux/interfaces';
 import { showSuccessAlert } from 'redux/reducers/alerts/alertsSlice';
 
-const apiClientConf = new Configuration(BASE_PARAMS);
-export const kafkaConnectApiClient = new KafkaConnectApi(apiClientConf);
-
 export const fetchConnects = createAsyncThunk<
   { connects: Connect[] },
   ClusterName

+ 6 - 10
kafka-ui-react-app/src/redux/reducers/consumerGroups/consumerGroupsSlice.ts

@@ -6,14 +6,12 @@ import {
   PayloadAction,
 } from '@reduxjs/toolkit';
 import {
-  Configuration,
   ConsumerGroupDetails,
   ConsumerGroupOrdering,
-  ConsumerGroupsApi,
   ConsumerGroupsPageResponse,
   SortOrder,
 } from 'generated-sources';
-import { BASE_PARAMS, AsyncRequestStatus } from 'lib/constants';
+import { AsyncRequestStatus } from 'lib/constants';
 import { getResponse } from 'lib/errorHandling';
 import {
   ClusterName,
@@ -23,9 +21,7 @@ import {
 } from 'redux/interfaces';
 import { createFetchingSelector } from 'redux/reducers/loader/selectors';
 import { EntityState } from '@reduxjs/toolkit/src/entities/models';
-
-const apiClientConf = new Configuration(BASE_PARAMS);
-export const api = new ConsumerGroupsApi(apiClientConf);
+import { consumerGroupsApiClient } from 'lib/api';
 
 export const fetchConsumerGroupsPaged = createAsyncThunk<
   ConsumerGroupsPageResponse,
@@ -44,7 +40,7 @@ export const fetchConsumerGroupsPaged = createAsyncThunk<
     { rejectWithValue }
   ) => {
     try {
-      const response = await api.getConsumerGroupsPageRaw({
+      const response = await consumerGroupsApiClient.getConsumerGroupsPageRaw({
         clusterName,
         orderBy,
         sortOrder,
@@ -66,7 +62,7 @@ export const fetchConsumerGroupDetails = createAsyncThunk<
   'consumerGroups/fetchConsumerGroupDetails',
   async ({ clusterName, consumerGroupID }, { rejectWithValue }) => {
     try {
-      return await api.getConsumerGroup({
+      return await consumerGroupsApiClient.getConsumerGroup({
         clusterName,
         id: consumerGroupID,
       });
@@ -83,7 +79,7 @@ export const deleteConsumerGroup = createAsyncThunk<
   'consumerGroups/deleteConsumerGroup',
   async ({ clusterName, consumerGroupID }, { rejectWithValue }) => {
     try {
-      await api.deleteConsumerGroup({
+      await consumerGroupsApiClient.deleteConsumerGroup({
         clusterName,
         id: consumerGroupID,
       });
@@ -105,7 +101,7 @@ export const resetConsumerGroupOffsets = createAsyncThunk<
     { rejectWithValue }
   ) => {
     try {
-      await api.resetConsumerGroupOffsets({
+      await consumerGroupsApiClient.resetConsumerGroupOffsets({
         clusterName,
         id: consumerGroupID,
         consumerGroupOffsetsReset: {

+ 0 - 2
kafka-ui-react-app/src/redux/reducers/index.ts

@@ -1,7 +1,6 @@
 import { combineReducers } from '@reduxjs/toolkit';
 import clusters from 'redux/reducers/clusters/clustersSlice';
 import loader from 'redux/reducers/loader/loaderSlice';
-import brokers from 'redux/reducers/brokers/brokersSlice';
 import alerts from 'redux/reducers/alerts/alertsSlice';
 import schemas from 'redux/reducers/schemas/schemasSlice';
 import connect from 'redux/reducers/connect/connectSlice';
@@ -16,7 +15,6 @@ export default combineReducers({
   topics,
   topicMessages,
   clusters,
-  brokers,
   consumerGroups,
   schemas,
   connect,

+ 2 - 10
kafka-ui-react-app/src/redux/reducers/ksqlDb/ksqlDbSlice.ts

@@ -1,16 +1,8 @@
 import { KsqlState } from 'redux/interfaces/ksqlDb';
 import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
-import { BASE_PARAMS } from 'lib/constants';
-import {
-  Configuration,
-  ExecuteKsqlRequest,
-  KsqlApi,
-  Table as KsqlTable,
-} from 'generated-sources';
+import { ExecuteKsqlRequest, Table as KsqlTable } from 'generated-sources';
 import { ClusterName } from 'redux/interfaces';
-
-const apiClientConf = new Configuration(BASE_PARAMS);
-export const ksqlDbApiClient = new KsqlApi(apiClientConf);
+import { ksqlDbApiClient } from 'lib/api';
 
 export const transformKsqlResponse = (
   rawTable: Required<KsqlTable>

+ 1 - 5
kafka-ui-react-app/src/redux/reducers/loader/loaderSlice.ts

@@ -4,13 +4,9 @@ import {
   UnknownAsyncThunkPendingAction,
   UnknownAsyncThunkRejectedAction,
 } from '@reduxjs/toolkit/dist/matchers';
-import { ClustersApi, Configuration } from 'generated-sources';
-import { BASE_PARAMS, AsyncRequestStatus } from 'lib/constants';
+import { AsyncRequestStatus } from 'lib/constants';
 import { LoaderSliceState } from 'redux/interfaces';
 
-const apiClientConf = new Configuration(BASE_PARAMS);
-export const clustersApiClient = new ClustersApi(apiClientConf);
-
 export const initialState: LoaderSliceState = {};
 
 export const loaderSlice = createSlice({

+ 2 - 6
kafka-ui-react-app/src/redux/reducers/schemas/schemasSlice.ts

@@ -5,21 +5,17 @@ import {
   createSlice,
 } from '@reduxjs/toolkit';
 import {
-  Configuration,
-  SchemasApi,
   SchemaSubject,
   SchemaSubjectsResponse,
   GetSchemasRequest,
   GetLatestSchemaRequest,
 } from 'generated-sources';
-import { BASE_PARAMS, AsyncRequestStatus } from 'lib/constants';
+import { schemasApiClient } from 'lib/api';
+import { AsyncRequestStatus } from 'lib/constants';
 import { getResponse } from 'lib/errorHandling';
 import { ClusterName, RootState } from 'redux/interfaces';
 import { createFetchingSelector } from 'redux/reducers/loader/selectors';
 
-const apiClientConf = new Configuration(BASE_PARAMS);
-export const schemasApiClient = new SchemasApi(apiClientConf);
-
 export const SCHEMA_LATEST_FETCH_ACTION = 'schemas/latest/fetch';
 export const fetchLatestSchema = createAsyncThunk<
   SchemaSubject,

+ 2 - 5
kafka-ui-react-app/src/redux/reducers/topicMessages/topicMessagesSlice.ts

@@ -1,13 +1,10 @@
 import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
 import { TopicMessagesState, ClusterName, TopicName } from 'redux/interfaces';
-import { TopicMessage, Configuration, MessagesApi } from 'generated-sources';
-import { BASE_PARAMS } from 'lib/constants';
+import { TopicMessage } from 'generated-sources';
 import { getResponse } from 'lib/errorHandling';
 import { showSuccessAlert } from 'redux/reducers/alerts/alertsSlice';
 import { fetchTopicDetails } from 'redux/reducers/topics/topicsSlice';
-
-const apiClientConf = new Configuration(BASE_PARAMS);
-export const messagesApiClient = new MessagesApi(apiClientConf);
+import { messagesApiClient } from 'lib/api';
 
 export const clearTopicMessages = createAsyncThunk<
   undefined,

+ 8 - 12
kafka-ui-react-app/src/redux/reducers/topics/topicsSlice.ts

@@ -1,9 +1,6 @@
 import { v4 } from 'uuid';
 import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
 import {
-  Configuration,
-  TopicsApi,
-  ConsumerGroupsApi,
   TopicsResponse,
   TopicDetails,
   GetTopicsRequest,
@@ -18,7 +15,6 @@ import {
   RecreateTopicRequest,
   SortOrder,
   TopicColumnsToSort,
-  MessagesApi,
   GetTopicSchemaRequest,
   TopicMessageSchema,
 } from 'generated-sources';
@@ -30,15 +26,14 @@ import {
   TopicFormDataRaw,
   ClusterName,
 } from 'redux/interfaces';
-import { BASE_PARAMS } from 'lib/constants';
 import { getResponse } from 'lib/errorHandling';
 import { clearTopicMessages } from 'redux/reducers/topicMessages/topicMessagesSlice';
 import { showSuccessAlert } from 'redux/reducers/alerts/alertsSlice';
-
-const apiClientConf = new Configuration(BASE_PARAMS);
-const topicsApiClient = new TopicsApi(apiClientConf);
-const topicConsumerGroupsApiClient = new ConsumerGroupsApi(apiClientConf);
-const messagesApiClient = new MessagesApi(apiClientConf);
+import {
+  consumerGroupsApiClient,
+  messagesApiClient,
+  topicsApiClient,
+} from 'lib/api';
 
 export const fetchTopicsList = createAsyncThunk<
   TopicsResponse,
@@ -143,8 +138,9 @@ export const fetchTopicConsumerGroups = createAsyncThunk<
 >('topic/fetchTopicConsumerGroups', async (payload, { rejectWithValue }) => {
   try {
     const { topicName } = payload;
-    const consumerGroups =
-      await topicConsumerGroupsApiClient.getTopicConsumerGroups(payload);
+    const consumerGroups = await consumerGroupsApiClient.getTopicConsumerGroups(
+      payload
+    );
 
     return { consumerGroups, topicName };
   } catch (err) {