diff --git a/awx/ui_next/src/screens/Inventory/InventoryDetail/InventoryDetail.jsx b/awx/ui_next/src/screens/Inventory/InventoryDetail/InventoryDetail.jsx
index 3788dc130f..a9d558624c 100644
--- a/awx/ui_next/src/screens/Inventory/InventoryDetail/InventoryDetail.jsx
+++ b/awx/ui_next/src/screens/Inventory/InventoryDetail/InventoryDetail.jsx
@@ -1,4 +1,4 @@
-import React, { useState, useEffect, useRef } from 'react';
+import React, { useCallback } from 'react';
import { Link, useHistory } from 'react-router-dom';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
@@ -11,37 +11,19 @@ import DeleteButton from '@components/DeleteButton';
import ContentError from '@components/ContentError';
import ContentLoading from '@components/ContentLoading';
import { InventoriesAPI } from '@api';
+import useEndpoint from './useEndpoint';
import { Inventory } from '../../../types';
function InventoryDetail({ inventory, i18n }) {
- const [instanceGroups, setInstanceGroups] = useState([]);
- const [hasContentLoading, setHasContentLoading] = useState(true);
- const [contentError, setContentError] = useState(null);
const history = useHistory();
- const isMounted = useRef(null);
- useEffect(() => {
- isMounted.current = true;
- (async () => {
- setHasContentLoading(true);
- try {
- const { data } = await InventoriesAPI.readInstanceGroups(inventory.id);
- if (!isMounted.current) {
- return;
- }
- setInstanceGroups(data.results);
- } catch (err) {
- setContentError(err);
- } finally {
- if (isMounted.current) {
- setHasContentLoading(false);
- }
- }
- })();
- return () => {
- isMounted.current = false;
- };
- }, [inventory.id]);
+ const { results: instanceGroups, isLoading, error } = useEndpoint(
+ useCallback(async () => {
+ const { data } = await InventoriesAPI.readInstanceGroups(inventory.id);
+ return data.results;
+ }, [inventory.id]),
+ inventory.id
+ );
const deleteInventory = async () => {
await InventoriesAPI.destroy(inventory.id);
@@ -53,12 +35,12 @@ function InventoryDetail({ inventory, i18n }) {
user_capabilities: userCapabilities,
} = inventory.summary_fields;
- if (hasContentLoading) {
+ if (isLoading) {
return ;
}
- if (contentError) {
- return ;
+ if (error) {
+ return ;
}
return (
diff --git a/awx/ui_next/src/screens/Inventory/InventoryDetail/useEndpoint.js b/awx/ui_next/src/screens/Inventory/InventoryDetail/useEndpoint.js
new file mode 100644
index 0000000000..9c45d7e32e
--- /dev/null
+++ b/awx/ui_next/src/screens/Inventory/InventoryDetail/useEndpoint.js
@@ -0,0 +1,40 @@
+import { useEffect, useState, useRef } from 'react';
+
+export default function useEndpoint(fetch) {
+ const [results, setResults] = useState([]);
+ const [error, setError] = useState(null);
+ const [isLoading, setIsLoading] = useState(true);
+ const isMounted = useRef(null);
+
+ useEffect(() => {
+ isMounted.current = true;
+ (async () => {
+ // Do we want this set here or not? Can result in extra
+ // unmounting/re-mounting of child components
+ setIsLoading(true);
+ try {
+ const fetchedResults = await fetch();
+ if (isMounted.current) {
+ setResults(fetchedResults);
+ }
+ } catch (err) {
+ if (isMounted.current) {
+ setError(err);
+ }
+ } finally {
+ if (isMounted.current) {
+ setIsLoading(false);
+ }
+ }
+ })();
+ return () => {
+ isMounted.current = false;
+ };
+ }, [fetch]);
+
+ return {
+ results,
+ isLoading,
+ error,
+ };
+}