diff --git a/app/javascript/components/AeInlineMethod/NamespaceSelector.jsx b/app/javascript/components/AeInlineMethod/NamespaceSelector.jsx index ac0a620b893..3b8337229ff 100644 --- a/app/javascript/components/AeInlineMethod/NamespaceSelector.jsx +++ b/app/javascript/components/AeInlineMethod/NamespaceSelector.jsx @@ -1,6 +1,8 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState } from 'react'; import PropTypes from 'prop-types'; +import { useQuery } from 'react-query'; import { Loading } from 'carbon-components-react'; +import { debounce } from 'lodash'; import FilterNamespace from './FilterNamespace'; import MiqDataTable from '../miq-data-table'; import NotificationMessage from '../notification-message'; @@ -10,93 +12,90 @@ import { } from './helper'; import './style.scss'; -/** Component to search and select AeMethods. */ const NamespaceSelector = ({ onSelectMethod, selectedIds }) => { - const [data, setData] = useState({ - domains: [], - methods: [], - loading: true, - searchText: undefined, - selectedDomain: undefined, - }); + const [filterData, setFilterData] = useState({ searchText: '', selectedDomain: '' }); - /** Function to update the component state data. */ - const updateData = (updatedData) => setData({ ...data, ...updatedData }); + /** Loads the domains and stores in domainData for 60 seconds. */ + const { data: domainsData, isLoading: domainsLoading } = useQuery( + 'domainsData', + async() => (await http.get(namespaceUrls.aeDomainsUrl)).domains, + { + staleTime: 60000, + } + ); - /** Loads the 'domains' and 'methods' from its respective URL's during the component's onLoad event. */ - useEffect(() => { - Promise.all([ - http.get(namespaceUrls.aeDomainsUrl), - http.get(namespaceUrls.aeMethodsUrl)]) - .then(([{ domains }, { methods }]) => updateData({ loading: false, domains, methods: formatMethods(methods) })); - }, []); + /** Loads the methods and stores in methodsData for 60 seconds. + * If condition works on page load + * Else part would work if there is a change in filterData. + */ + const { data, isLoading: methodsLoading } = useQuery( + ['methodsData', filterData.searchText, filterData.selectedDomain], + async() => { + if (!filterData.searchText && !filterData.selectedDomain) { + const response = await http.get(namespaceUrls.aeMethodsUrl); + return formatMethods(response.methods); + } + const url = searchUrl(filterData.selectedDomain, filterData.searchText); + const response = await http.get(url); + return formatMethods(response.methods); + }, + { + keepPreviousData: true, + refetchOnWindowFocus: false, + staleTime: 60000, + } + ); - const retrieveMethods = async(url) => { - const response = await http.get(url); - return response.methods; - }; + /** Debounce the search text by delaying the text input provided to the API. */ + const debouncedSearch = debounce((newFilterData) => { + setFilterData(newFilterData); + }, 300); - /** Function to handle search text and drop-down item onchange events. */ - const onSearch = async(filterData) => { - updateData({ loading: true }); - const searchText = filterData.searchText ? filterData.searchText : data.searchText; - const selectedDomain = filterData.selectedDomain ? filterData.selectedDomain : data.selectedDomain; - const url = searchUrl(selectedDomain, searchText); - try { - const methods = await retrieveMethods(url); - updateData({ - loading: false, - selectedDomain, - searchText, - methods: formatMethods(methods), - }); - } catch (error) { - console.error('Error retrieving methods:', error); - updateData({ loading: false }); // Update loading state even if there's an error - } - }; + /** Function to handle the onSearch event during a filter change event. */ + const onSearch = (newFilterData) => debouncedSearch(newFilterData); - /** Function to handle the click events for the list. */ + /** Function to handle the click event of a cell in the data table. */ const onCellClick = (selectedRow, cellType, checked) => { const selectedItems = cellType === CellAction.selectAll - ? data.methods.map((item) => item.id) + ? data && data.map((item) => item.id) : [selectedRow]; onSelectMethod({ selectedItems, cellType, checked }); }; - /** Function to render the contents of the list. */ - const renderContents = () => (data.methods && data.methods.length > 0 - ? ( + const renderContents = () => { + if (!data || data.length === 0) { + return ; + } + + return ( onCellClick(selectedRow, cellType, event.target.checked)} /> - ) - : ); + ); + }; return (
- onSearch(filterData)} /> +
- { - data.loading - ? - : renderContents() - } + {(domainsLoading || methodsLoading) + ? + : renderContents()}
); }; -export default NamespaceSelector; - NamespaceSelector.propTypes = { onSelectMethod: PropTypes.func.isRequired, selectedIds: PropTypes.arrayOf(PropTypes.any).isRequired, }; + +export default NamespaceSelector; diff --git a/app/javascript/components/AeInlineMethod/index.jsx b/app/javascript/components/AeInlineMethod/index.jsx index adabaf247d3..05a152b1a09 100644 --- a/app/javascript/components/AeInlineMethod/index.jsx +++ b/app/javascript/components/AeInlineMethod/index.jsx @@ -1,4 +1,5 @@ import React, { useState } from 'react'; +import { QueryClient, QueryClientProvider } from 'react-query'; import PropTypes from 'prop-types'; import { Modal, Button, ModalBody, Accordion, AccordionItem, @@ -12,6 +13,8 @@ import { formatListMethods, methodListHeaders, namespaceUrls } from './helper'; /** Component to render a tree and to select an embedded method. */ const AeInlineMethod = ({ type }) => { + const queryClient = new QueryClient(); + const [data, setData] = useState({ isModalOpen: false, selectedIds: [], @@ -80,10 +83,12 @@ const AeInlineMethod = ({ type }) => { { data.isModalOpen && ( - onSelectMethod(selectedItems, cellType, checked)} - selectedIds={data.selectedIds} - /> + + onSelectMethod(selectedItems, cellType, checked)} + selectedIds={data.selectedIds} + /> + ) } diff --git a/package.json b/package.json index 2f12a67e0d3..0ac5c314af8 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "jquery-ujs": "~1.2.2", "jquery.hotkeys": "~0.1.0", "jquery.observe_field": "~0.1.0", + "loadash": "^1.0.0", "lodash": "~4.17.10", "moment": "~2.29.4", "moment-duration-format": "~2.2.2", @@ -90,6 +91,7 @@ "react-codemirror2": "^6.0.0", "react-dom": "~16.13.1", "react-markdown": "6.0.0", + "react-query": "^3.39.3", "react-redux": "^7.1.1", "react-router": "~5.1.2", "react-router-dom": "~5.1.2",