diff --git a/.eslintrc b/.eslintrc index 97fcee4893e..e590e63be15 100644 --- a/.eslintrc +++ b/.eslintrc @@ -19,6 +19,18 @@ "alphabetize": { "order": "asc" } } ], + "no-restricted-imports": [ + "warn", + { + "paths": [ + { + "name": "react-redux", + "importNames": ["useDispatch", "useSelector"], + "message": "Please import from app/types instead." + } + ] + } + ], // Use typescript's no-redeclare for compatibility with overrides "no-redeclare": "off", diff --git a/packages/grafana-data/src/types/explore.ts b/packages/grafana-data/src/types/explore.ts index 4e0bd6a7d61..abc73743db0 100644 --- a/packages/grafana-data/src/types/explore.ts +++ b/packages/grafana-data/src/types/explore.ts @@ -21,9 +21,14 @@ export interface ExploreTracePanelState { spanId?: string; } +export interface SplitOpenOptions { + datasourceUid: string; + query: T; + range?: TimeRange; + panelsState?: ExplorePanelsState; +} + /** * SplitOpen type is used in Explore and related components. */ -export type SplitOpen = ( - options?: { datasourceUid: string; query: T; range?: TimeRange; panelsState?: ExplorePanelsState } | undefined -) => void; +export type SplitOpen = (options?: SplitOpenOptions | undefined) => void; diff --git a/public/app/core/components/AppChrome/TopSearchBar.tsx b/public/app/core/components/AppChrome/TopSearchBar.tsx index 6c2788dfaff..87f7cff44af 100644 --- a/public/app/core/components/AppChrome/TopSearchBar.tsx +++ b/public/app/core/components/AppChrome/TopSearchBar.tsx @@ -1,7 +1,6 @@ import { css } from '@emotion/css'; import { cloneDeep } from 'lodash'; import React, { useState } from 'react'; -import { useSelector } from 'react-redux'; import { useLocation } from 'react-router-dom'; import { GrafanaTheme2, NavSection } from '@grafana/data'; @@ -9,7 +8,7 @@ import { locationService } from '@grafana/runtime'; import { Dropdown, FilterInput, Icon, Tooltip, useStyles2 } from '@grafana/ui'; import { contextSrv } from 'app/core/core'; import { useSearchQuery } from 'app/features/search/hooks/useSearchQuery'; -import { StoreState } from 'app/types'; +import { useSelector } from 'app/types'; import { enrichConfigItems, enrichWithInteractionTracking } from '../NavBar/utils'; import { OrgSwitcher } from '../OrgSwitcher'; @@ -21,7 +20,7 @@ export function TopSearchBar() { const styles = useStyles2(getStyles); const location = useLocation(); const { query, onQueryChange } = useSearchQuery({}); - const navBarTree = useSelector((state: StoreState) => state.navBarTree); + const navBarTree = useSelector((state) => state.navBarTree); const navTree = cloneDeep(navBarTree); const [showSwitcherModal, setShowSwitcherModal] = useState(false); const toggleSwitcherModal = () => { diff --git a/public/app/core/components/MegaMenu/MegaMenu.tsx b/public/app/core/components/MegaMenu/MegaMenu.tsx index 894949df2c4..c5be1d645e7 100644 --- a/public/app/core/components/MegaMenu/MegaMenu.tsx +++ b/public/app/core/components/MegaMenu/MegaMenu.tsx @@ -1,13 +1,12 @@ import { css } from '@emotion/css'; import { cloneDeep } from 'lodash'; import React, { useState } from 'react'; -import { useSelector } from 'react-redux'; import { useLocation } from 'react-router-dom'; import { GrafanaTheme2, NavModelItem, NavSection } from '@grafana/data'; import { config } from '@grafana/runtime'; import { useTheme2 } from '@grafana/ui'; -import { StoreState } from 'app/types'; +import { useSelector } from 'app/types'; import { enrichConfigItems, enrichWithInteractionTracking, getActiveItem } from '../NavBar/utils'; @@ -19,7 +18,7 @@ export interface Props { } export const MegaMenu = React.memo(({ onClose, searchBarHidden }) => { - const navBarTree = useSelector((state: StoreState) => state.navBarTree); + const navBarTree = useSelector((state) => state.navBarTree); const theme = useTheme2(); const styles = getStyles(theme); const location = useLocation(); diff --git a/public/app/core/components/NavBar/NavBar.tsx b/public/app/core/components/NavBar/NavBar.tsx index a1af49b439d..66c084f5cb0 100644 --- a/public/app/core/components/NavBar/NavBar.tsx +++ b/public/app/core/components/NavBar/NavBar.tsx @@ -3,14 +3,13 @@ import { FocusScope } from '@react-aria/focus'; import { Location as HistoryLocation } from 'history'; import { cloneDeep } from 'lodash'; import React, { useState } from 'react'; -import { useSelector } from 'react-redux'; import { useLocation } from 'react-router-dom'; import { GrafanaTheme2, NavModelItem, NavSection } from '@grafana/data'; import { config, locationSearchToObject, locationService, reportInteraction } from '@grafana/runtime'; import { Icon, useTheme2, CustomScrollbar } from '@grafana/ui'; import { getKioskMode } from 'app/core/navigation/kiosk'; -import { StoreState } from 'app/types'; +import { useSelector } from 'app/types'; import { OrgSwitcher } from '../OrgSwitcher'; @@ -35,7 +34,7 @@ const onOpenSearch = () => { }; export const NavBar = React.memo(() => { - const navBarTree = useSelector((state: StoreState) => state.navBarTree); + const navBarTree = useSelector((state) => state.navBarTree); const theme = useTheme2(); const styles = getStyles(theme); const location = useLocation(); diff --git a/public/app/core/components/Page/usePageNav.ts b/public/app/core/components/Page/usePageNav.ts index e78a5d17aa6..da6bd0da218 100644 --- a/public/app/core/components/Page/usePageNav.ts +++ b/public/app/core/components/Page/usePageNav.ts @@ -1,10 +1,9 @@ -import { useSelector } from 'react-redux'; import { createSelector } from 'reselect'; import { NavModel } from '@grafana/data'; import { getNavModel } from 'app/core/selectors/navModel'; import { store } from 'app/store/store'; -import { StoreState } from 'app/types'; +import { StoreState, useSelector } from 'app/types'; export function usePageNav(navId?: string, oldProp?: NavModel): NavModel | undefined { if (oldProp) { diff --git a/public/app/core/components/connectWithCleanUp.tsx b/public/app/core/components/connectWithCleanUp.tsx index 94506e0383a..135e692bd49 100644 --- a/public/app/core/components/connectWithCleanUp.tsx +++ b/public/app/core/components/connectWithCleanUp.tsx @@ -1,6 +1,8 @@ import hoistNonReactStatics from 'hoist-non-react-statics'; import React, { ComponentType, FunctionComponent, useEffect } from 'react'; -import { connect, MapDispatchToPropsParam, MapStateToPropsParam, useDispatch } from 'react-redux'; +import { connect, MapDispatchToPropsParam, MapStateToPropsParam } from 'react-redux'; + +import { useDispatch } from 'app/types'; import { cleanUpAction, CleanUpAction } from '../actions/cleanUp'; diff --git a/public/app/core/hooks/useCleanup.ts b/public/app/core/hooks/useCleanup.ts index aa8f97a9a2d..86696b829d2 100644 --- a/public/app/core/hooks/useCleanup.ts +++ b/public/app/core/hooks/useCleanup.ts @@ -1,5 +1,6 @@ import { useEffect, useRef } from 'react'; -import { useDispatch } from 'react-redux'; + +import { useDispatch } from 'app/types'; import { cleanUpAction, CleanUpAction } from '../actions/cleanUp'; diff --git a/public/app/core/hooks/useNavModel.ts b/public/app/core/hooks/useNavModel.ts index 99917e14262..047654109cc 100644 --- a/public/app/core/hooks/useNavModel.ts +++ b/public/app/core/hooks/useNavModel.ts @@ -1,11 +1,9 @@ -import { useSelector } from 'react-redux'; - import { NavModel } from '@grafana/data'; -import { StoreState } from 'app/types/store'; +import { useSelector } from 'app/types'; import { getNavModel } from '../selectors/navModel'; export const useNavModel = (id: string): NavModel => { - const navIndex = useSelector((state: StoreState) => state.navIndex); + const navIndex = useSelector((state) => state.navIndex); return getNavModel(navIndex, id); }; diff --git a/public/app/features/alerting/unified/AlertGroups.tsx b/public/app/features/alerting/unified/AlertGroups.tsx index 9aee9d3c81b..dab1d1b4eb8 100644 --- a/public/app/features/alerting/unified/AlertGroups.tsx +++ b/public/app/features/alerting/unified/AlertGroups.tsx @@ -1,10 +1,10 @@ import { css } from '@emotion/css'; import React, { useEffect } from 'react'; -import { useDispatch } from 'react-redux'; import { GrafanaTheme2 } from '@grafana/data'; import { Alert, LoadingPlaceholder, useStyles2 } from '@grafana/ui'; import { useQueryParams } from 'app/core/hooks/useQueryParams'; +import { useDispatch } from 'app/types'; import { AlertingPageWrapper } from './components/AlertingPageWrapper'; import { NoAlertManagerWarning } from './components/NoAlertManagerWarning'; diff --git a/public/app/features/alerting/unified/AlertsFolderView.tsx b/public/app/features/alerting/unified/AlertsFolderView.tsx index b0cc96cfef3..94d15495208 100644 --- a/public/app/features/alerting/unified/AlertsFolderView.tsx +++ b/public/app/features/alerting/unified/AlertsFolderView.tsx @@ -1,14 +1,13 @@ import { css } from '@emotion/css'; import { isEqual, orderBy, uniqWith } from 'lodash'; import React, { useEffect, useState } from 'react'; -import { useDispatch } from 'react-redux'; import { useDebounce } from 'react-use'; import { GrafanaTheme2, SelectableValue } from '@grafana/data'; import { Card, FilterInput, Icon, Pagination, Select, Stack, TagList, useStyles2 } from '@grafana/ui'; import { DEFAULT_PER_PAGE_PAGINATION } from 'app/core/constants'; import { getQueryParamValue } from 'app/core/utils/query'; -import { FolderState } from 'app/types'; +import { FolderState, useDispatch } from 'app/types'; import { CombinedRule } from 'app/types/unified-alerting'; import { useCombinedRuleNamespaces } from './hooks/useCombinedRuleNamespaces'; diff --git a/public/app/features/alerting/unified/AmRoutes.tsx b/public/app/features/alerting/unified/AmRoutes.tsx index b1cb82d68dd..dabe5b5aa8b 100644 --- a/public/app/features/alerting/unified/AmRoutes.tsx +++ b/public/app/features/alerting/unified/AmRoutes.tsx @@ -1,10 +1,10 @@ import { css } from '@emotion/css'; import React, { FC, useCallback, useEffect, useMemo, useState } from 'react'; -import { useDispatch } from 'react-redux'; import { GrafanaTheme2 } from '@grafana/data'; import { Alert, LoadingPlaceholder, useStyles2, withErrorBoundary } from '@grafana/ui'; import { Receiver } from 'app/plugins/datasource/alertmanager/types'; +import { useDispatch } from 'app/types'; import { useCleanup } from '../../../core/hooks/useCleanup'; diff --git a/public/app/features/alerting/unified/MuteTimings.tsx b/public/app/features/alerting/unified/MuteTimings.tsx index 89349196c59..eeaf80ea877 100644 --- a/public/app/features/alerting/unified/MuteTimings.tsx +++ b/public/app/features/alerting/unified/MuteTimings.tsx @@ -1,10 +1,10 @@ import React, { useCallback, useEffect } from 'react'; -import { useDispatch } from 'react-redux'; import { Route, Redirect, Switch } from 'react-router-dom'; import { Alert, LoadingPlaceholder } from '@grafana/ui'; import { useQueryParams } from 'app/core/hooks/useQueryParams'; import { MuteTimeInterval } from 'app/plugins/datasource/alertmanager/types'; +import { useDispatch } from 'app/types'; import MuteTimingForm from './components/amroutes/MuteTimingForm'; import { useAlertManagerSourceName } from './hooks/useAlertManagerSourceName'; diff --git a/public/app/features/alerting/unified/Receivers.tsx b/public/app/features/alerting/unified/Receivers.tsx index be751cf0400..e7a431323e1 100644 --- a/public/app/features/alerting/unified/Receivers.tsx +++ b/public/app/features/alerting/unified/Receivers.tsx @@ -1,9 +1,9 @@ import React, { FC, useEffect } from 'react'; -import { useDispatch } from 'react-redux'; import { Redirect, Route, RouteChildrenProps, Switch, useLocation, useParams } from 'react-router-dom'; import { NavModelItem } from '@grafana/data'; import { Alert, LoadingPlaceholder, withErrorBoundary } from '@grafana/ui'; +import { useDispatch } from 'app/types'; import { AlertManagerPicker } from './components/AlertManagerPicker'; import { AlertingPageWrapper } from './components/AlertingPageWrapper'; diff --git a/public/app/features/alerting/unified/RuleEditor.tsx b/public/app/features/alerting/unified/RuleEditor.tsx index 892896be70f..dd8d83ae705 100644 --- a/public/app/features/alerting/unified/RuleEditor.tsx +++ b/public/app/features/alerting/unified/RuleEditor.tsx @@ -1,6 +1,5 @@ import { css } from '@emotion/css'; import React, { FC, useEffect } from 'react'; -import { useDispatch } from 'react-redux'; import { useAsync } from 'react-use'; import { GrafanaTheme2 } from '@grafana/data'; @@ -8,6 +7,7 @@ import { Alert, LinkButton, LoadingPlaceholder, useStyles2, withErrorBoundary } import { Page } from 'app/core/components/Page/Page'; import { useCleanup } from 'app/core/hooks/useCleanup'; import { GrafanaRouteComponentProps } from 'app/core/navigation/types'; +import { useDispatch } from 'app/types'; import { RuleIdentifier } from 'app/types/unified-alerting'; import { AlertRuleForm } from './components/rule-editor/AlertRuleForm'; diff --git a/public/app/features/alerting/unified/RuleList.tsx b/public/app/features/alerting/unified/RuleList.tsx index cb299fdfc3e..fc3ee1c17e4 100644 --- a/public/app/features/alerting/unified/RuleList.tsx +++ b/public/app/features/alerting/unified/RuleList.tsx @@ -1,11 +1,11 @@ import { css } from '@emotion/css'; import React, { useEffect, useMemo, useState } from 'react'; -import { useDispatch } from 'react-redux'; import { useLocation } from 'react-router-dom'; import { GrafanaTheme2, urlUtil } from '@grafana/data'; import { Button, LinkButton, useStyles2, withErrorBoundary } from '@grafana/ui'; import { useQueryParams } from 'app/core/hooks/useQueryParams'; +import { useDispatch } from 'app/types'; import { AlertingPageWrapper } from './components/AlertingPageWrapper'; import { NoRulesSplash } from './components/rules/NoRulesCTA'; diff --git a/public/app/features/alerting/unified/Silences.tsx b/public/app/features/alerting/unified/Silences.tsx index 76587e0cd40..8f12db15b24 100644 --- a/public/app/features/alerting/unified/Silences.tsx +++ b/public/app/features/alerting/unified/Silences.tsx @@ -1,9 +1,9 @@ import React, { FC, useCallback, useEffect } from 'react'; -import { useDispatch } from 'react-redux'; import { Redirect, Route, RouteChildrenProps, Switch, useLocation } from 'react-router-dom'; import { Alert, LoadingPlaceholder, withErrorBoundary } from '@grafana/ui'; import { Silence } from 'app/plugins/datasource/alertmanager/types'; +import { useDispatch } from 'app/types'; import { featureDiscoveryApi } from './api/featureDiscoveryApi'; import { AlertManagerPicker } from './components/AlertManagerPicker'; diff --git a/public/app/features/alerting/unified/components/admin/AlertmanagerConfig.tsx b/public/app/features/alerting/unified/components/admin/AlertmanagerConfig.tsx index bdb3e0cc5cb..959e55d4d3a 100644 --- a/public/app/features/alerting/unified/components/admin/AlertmanagerConfig.tsx +++ b/public/app/features/alerting/unified/components/admin/AlertmanagerConfig.tsx @@ -1,9 +1,9 @@ import { css } from '@emotion/css'; import React, { useEffect, useState, useMemo } from 'react'; -import { useDispatch } from 'react-redux'; import { GrafanaTheme2 } from '@grafana/data'; import { Alert, Button, ConfirmModal, TextArea, HorizontalGroup, Field, Form, useStyles2 } from '@grafana/ui'; +import { useDispatch } from 'app/types'; import { useAlertManagerSourceName } from '../../hooks/useAlertManagerSourceName'; import { useAlertManagersByPermission } from '../../hooks/useAlertManagerSources'; diff --git a/public/app/features/alerting/unified/components/admin/ExternalAlertmanagers.tsx b/public/app/features/alerting/unified/components/admin/ExternalAlertmanagers.tsx index 3321b48f13d..9c5002eadc6 100644 --- a/public/app/features/alerting/unified/components/admin/ExternalAlertmanagers.tsx +++ b/public/app/features/alerting/unified/components/admin/ExternalAlertmanagers.tsx @@ -1,6 +1,5 @@ import { css, cx } from '@emotion/css'; import React, { useCallback, useEffect, useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; import { GrafanaTheme2, SelectableValue } from '@grafana/data'; import { @@ -18,6 +17,7 @@ import { import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; import { loadDataSources } from 'app/features/datasources/state/actions'; import { AlertmanagerChoice } from 'app/plugins/datasource/alertmanager/types'; +import { useDispatch, useSelector } from 'app/types'; import { StoreState } from 'app/types/store'; import { useExternalAmSelector, useExternalDataSourceAlertmanagers } from '../../hooks/useExternalAmSelector'; diff --git a/public/app/features/alerting/unified/components/amroutes/MuteTimingForm.tsx b/public/app/features/alerting/unified/components/amroutes/MuteTimingForm.tsx index 4bd2c2c0aeb..1048b21c4f7 100644 --- a/public/app/features/alerting/unified/components/amroutes/MuteTimingForm.tsx +++ b/public/app/features/alerting/unified/components/amroutes/MuteTimingForm.tsx @@ -1,7 +1,6 @@ import { css } from '@emotion/css'; import React, { useMemo } from 'react'; import { FormProvider, useForm } from 'react-hook-form'; -import { useDispatch } from 'react-redux'; import { GrafanaTheme2 } from '@grafana/data'; import { Alert, Field, FieldSet, Input, Button, LinkButton, useStyles2 } from '@grafana/ui'; @@ -10,6 +9,7 @@ import { AlertManagerCortexConfig, MuteTimeInterval, } from 'app/plugins/datasource/alertmanager/types'; +import { useDispatch } from 'app/types'; import { useAlertManagerSourceName } from '../../hooks/useAlertManagerSourceName'; import { useAlertManagersByPermission } from '../../hooks/useAlertManagerSources'; diff --git a/public/app/features/alerting/unified/components/amroutes/MuteTimingsTable.tsx b/public/app/features/alerting/unified/components/amroutes/MuteTimingsTable.tsx index 7f5b6ef2106..6569a083047 100644 --- a/public/app/features/alerting/unified/components/amroutes/MuteTimingsTable.tsx +++ b/public/app/features/alerting/unified/components/amroutes/MuteTimingsTable.tsx @@ -1,11 +1,11 @@ import { css } from '@emotion/css'; import React, { FC, useMemo, useState } from 'react'; -import { useDispatch } from 'react-redux'; import { GrafanaTheme2 } from '@grafana/data'; import { IconButton, LinkButton, Link, useStyles2, ConfirmModal } from '@grafana/ui'; import { contextSrv } from 'app/core/services/context_srv'; import { AlertManagerCortexConfig, MuteTimeInterval, TimeInterval } from 'app/plugins/datasource/alertmanager/types'; +import { useDispatch } from 'app/types'; import { Authorize } from '../../components/Authorize'; import { useUnifiedAlertingSelector } from '../../hooks/useUnifiedAlertingSelector'; diff --git a/public/app/features/alerting/unified/components/panel-alerts-tab/NewRuleFromPanelButton.tsx b/public/app/features/alerting/unified/components/panel-alerts-tab/NewRuleFromPanelButton.tsx index 608f830987d..20d394bbb65 100644 --- a/public/app/features/alerting/unified/components/panel-alerts-tab/NewRuleFromPanelButton.tsx +++ b/public/app/features/alerting/unified/components/panel-alerts-tab/NewRuleFromPanelButton.tsx @@ -1,12 +1,11 @@ import React, { FC } from 'react'; -import { useSelector } from 'react-redux'; import { useLocation } from 'react-router-dom'; import { useAsync } from 'react-use'; import { urlUtil } from '@grafana/data'; import { Alert, Button, LinkButton } from '@grafana/ui'; import { DashboardModel, PanelModel } from 'app/features/dashboard/state'; -import { StoreState } from 'app/types'; +import { useSelector } from 'app/types'; import { panelToRuleFormValues } from '../../utils/rule-form'; @@ -17,7 +16,7 @@ interface Props { } export const NewRuleFromPanelButton: FC = ({ dashboard, panel, className }) => { - const templating = useSelector((state: StoreState) => { + const templating = useSelector((state) => { return state.templating; }); diff --git a/public/app/features/alerting/unified/components/receivers/GlobalConfigForm.tsx b/public/app/features/alerting/unified/components/receivers/GlobalConfigForm.tsx index 8b66187b3db..a83a5e7d69f 100644 --- a/public/app/features/alerting/unified/components/receivers/GlobalConfigForm.tsx +++ b/public/app/features/alerting/unified/components/receivers/GlobalConfigForm.tsx @@ -1,10 +1,10 @@ import React, { FC } from 'react'; import { useForm, FormProvider } from 'react-hook-form'; -import { useDispatch } from 'react-redux'; import { Alert, Button, HorizontalGroup, LinkButton } from '@grafana/ui'; import { useCleanup } from 'app/core/hooks/useCleanup'; import { AlertManagerCortexConfig } from 'app/plugins/datasource/alertmanager/types'; +import { useDispatch } from 'app/types'; import { useUnifiedAlertingSelector } from '../../hooks/useUnifiedAlertingSelector'; import { updateAlertManagerConfigAction } from '../../state/actions'; diff --git a/public/app/features/alerting/unified/components/receivers/ReceiversTable.tsx b/public/app/features/alerting/unified/components/receivers/ReceiversTable.tsx index a7cfcbd6c5b..86b9c3e07e9 100644 --- a/public/app/features/alerting/unified/components/receivers/ReceiversTable.tsx +++ b/public/app/features/alerting/unified/components/receivers/ReceiversTable.tsx @@ -1,11 +1,11 @@ import { css } from '@emotion/css'; import React, { FC, useMemo, useState } from 'react'; -import { useDispatch } from 'react-redux'; import { GrafanaTheme2 } from '@grafana/data'; import { Button, ConfirmModal, Modal, useStyles2 } from '@grafana/ui'; import { contextSrv } from 'app/core/services/context_srv'; import { AlertManagerCortexConfig } from 'app/plugins/datasource/alertmanager/types'; +import { useDispatch } from 'app/types'; import { Authorize } from '../../components/Authorize'; import { useUnifiedAlertingSelector } from '../../hooks/useUnifiedAlertingSelector'; diff --git a/public/app/features/alerting/unified/components/receivers/TemplateForm.tsx b/public/app/features/alerting/unified/components/receivers/TemplateForm.tsx index e9b4dec7394..2535b08010d 100644 --- a/public/app/features/alerting/unified/components/receivers/TemplateForm.tsx +++ b/public/app/features/alerting/unified/components/receivers/TemplateForm.tsx @@ -1,13 +1,13 @@ import { css } from '@emotion/css'; import React, { FC } from 'react'; import { useForm, Validate } from 'react-hook-form'; -import { useDispatch } from 'react-redux'; import AutoSizer from 'react-virtualized-auto-sizer'; import { GrafanaTheme2 } from '@grafana/data'; import { Alert, Button, Field, FieldSet, Input, LinkButton, useStyles2 } from '@grafana/ui'; import { useCleanup } from 'app/core/hooks/useCleanup'; import { AlertManagerCortexConfig } from 'app/plugins/datasource/alertmanager/types'; +import { useDispatch } from 'app/types'; import { useUnifiedAlertingSelector } from '../../hooks/useUnifiedAlertingSelector'; import { updateAlertManagerConfigAction } from '../../state/actions'; diff --git a/public/app/features/alerting/unified/components/receivers/TemplatesTable.tsx b/public/app/features/alerting/unified/components/receivers/TemplatesTable.tsx index 375dc5f25c6..423fbf2fa8f 100644 --- a/public/app/features/alerting/unified/components/receivers/TemplatesTable.tsx +++ b/public/app/features/alerting/unified/components/receivers/TemplatesTable.tsx @@ -1,9 +1,9 @@ import React, { FC, Fragment, useMemo, useState } from 'react'; -import { useDispatch } from 'react-redux'; import { ConfirmModal, useStyles2 } from '@grafana/ui'; import { contextSrv } from 'app/core/services/context_srv'; import { AlertManagerCortexConfig } from 'app/plugins/datasource/alertmanager/types'; +import { useDispatch } from 'app/types'; import { Authorize } from '../../components/Authorize'; import { deleteTemplateAction } from '../../state/actions'; diff --git a/public/app/features/alerting/unified/components/receivers/form/CloudReceiverForm.tsx b/public/app/features/alerting/unified/components/receivers/form/CloudReceiverForm.tsx index 3737628c084..87549586611 100644 --- a/public/app/features/alerting/unified/components/receivers/form/CloudReceiverForm.tsx +++ b/public/app/features/alerting/unified/components/receivers/form/CloudReceiverForm.tsx @@ -1,8 +1,8 @@ import React, { FC, useMemo } from 'react'; -import { useDispatch } from 'react-redux'; import { Alert } from '@grafana/ui'; import { AlertManagerCortexConfig, Receiver } from 'app/plugins/datasource/alertmanager/types'; +import { useDispatch } from 'app/types'; import { updateAlertManagerConfigAction } from '../../../state/actions'; import { CloudChannelValues, ReceiverFormValues, CloudChannelMap } from '../../../types/receiver-form'; diff --git a/public/app/features/alerting/unified/components/receivers/form/GrafanaReceiverForm.tsx b/public/app/features/alerting/unified/components/receivers/form/GrafanaReceiverForm.tsx index 2ae7b222355..cfba5c26548 100644 --- a/public/app/features/alerting/unified/components/receivers/form/GrafanaReceiverForm.tsx +++ b/public/app/features/alerting/unified/components/receivers/form/GrafanaReceiverForm.tsx @@ -1,5 +1,4 @@ import React, { FC, useEffect, useMemo, useState } from 'react'; -import { useDispatch } from 'react-redux'; import { LoadingPlaceholder } from '@grafana/ui'; import { @@ -8,6 +7,7 @@ import { Receiver, TestReceiversAlert, } from 'app/plugins/datasource/alertmanager/types'; +import { useDispatch } from 'app/types'; import { useUnifiedAlertingSelector } from '../../../hooks/useUnifiedAlertingSelector'; import { diff --git a/public/app/features/alerting/unified/components/rule-editor/AlertRuleForm.tsx b/public/app/features/alerting/unified/components/rule-editor/AlertRuleForm.tsx index deb1483d35c..7c8a0d91469 100644 --- a/public/app/features/alerting/unified/components/rule-editor/AlertRuleForm.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/AlertRuleForm.tsx @@ -1,7 +1,6 @@ import { css } from '@emotion/css'; import React, { FC, useMemo, useState } from 'react'; import { FormProvider, useForm, UseFormWatch } from 'react-hook-form'; -import { useDispatch } from 'react-redux'; import { Link } from 'react-router-dom'; import { GrafanaTheme2 } from '@grafana/data'; @@ -9,6 +8,7 @@ import { Button, ConfirmModal, CustomScrollbar, PageToolbar, Spinner, useStyles2 import { useAppNotification } from 'app/core/copy/appNotification'; import { useCleanup } from 'app/core/hooks/useCleanup'; import { useQueryParams } from 'app/core/hooks/useQueryParams'; +import { useDispatch } from 'app/types'; import { RuleWithLocation } from 'app/types/unified-alerting'; import { useUnifiedAlertingSelector } from '../../hooks/useUnifiedAlertingSelector'; diff --git a/public/app/features/alerting/unified/components/rule-editor/GroupAndNamespaceFields.tsx b/public/app/features/alerting/unified/components/rule-editor/GroupAndNamespaceFields.tsx index 149cfb2361f..0462825f134 100644 --- a/public/app/features/alerting/unified/components/rule-editor/GroupAndNamespaceFields.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/GroupAndNamespaceFields.tsx @@ -1,10 +1,10 @@ import { css } from '@emotion/css'; import React, { FC, useEffect, useMemo, useState } from 'react'; import { useFormContext } from 'react-hook-form'; -import { useDispatch } from 'react-redux'; import { GrafanaTheme2, SelectableValue } from '@grafana/data'; import { Field, InputControl, useStyles2 } from '@grafana/ui'; +import { useDispatch } from 'app/types'; import { useUnifiedAlertingSelector } from '../../hooks/useUnifiedAlertingSelector'; import { fetchRulerRulesAction } from '../../state/actions'; diff --git a/public/app/features/alerting/unified/components/rules/EditRuleGroupModal.tsx b/public/app/features/alerting/unified/components/rules/EditRuleGroupModal.tsx index 39c6f86eab3..f2650b72f94 100644 --- a/public/app/features/alerting/unified/components/rules/EditRuleGroupModal.tsx +++ b/public/app/features/alerting/unified/components/rules/EditRuleGroupModal.tsx @@ -1,10 +1,10 @@ import { css } from '@emotion/css'; import React, { useEffect, useMemo } from 'react'; -import { useDispatch } from 'react-redux'; import { isValidGoDuration } from '@grafana/data'; import { Modal, Button, Form, Field, Input, useStyles2 } from '@grafana/ui'; import { useCleanup } from 'app/core/hooks/useCleanup'; +import { useDispatch } from 'app/types'; import { CombinedRuleGroup, CombinedRuleNamespace } from 'app/types/unified-alerting'; import { useUnifiedAlertingSelector } from '../../hooks/useUnifiedAlertingSelector'; diff --git a/public/app/features/alerting/unified/components/rules/RuleDetailsActionButtons.tsx b/public/app/features/alerting/unified/components/rules/RuleDetailsActionButtons.tsx index c657ab760ea..4ef5a98f33f 100644 --- a/public/app/features/alerting/unified/components/rules/RuleDetailsActionButtons.tsx +++ b/public/app/features/alerting/unified/components/rules/RuleDetailsActionButtons.tsx @@ -1,6 +1,5 @@ import { css } from '@emotion/css'; import React, { FC, Fragment, useState } from 'react'; -import { useDispatch } from 'react-redux'; import { useLocation } from 'react-router-dom'; import { GrafanaTheme2, urlUtil } from '@grafana/data'; @@ -8,7 +7,7 @@ import { config } from '@grafana/runtime'; import { Button, ClipboardButton, ConfirmModal, HorizontalGroup, LinkButton, useStyles2 } from '@grafana/ui'; import { useAppNotification } from 'app/core/copy/appNotification'; import { contextSrv } from 'app/core/services/context_srv'; -import { AccessControlAction } from 'app/types'; +import { AccessControlAction, useDispatch } from 'app/types'; import { CombinedRule, RulesSource } from 'app/types/unified-alerting'; import { useIsRuleEditable } from '../../hooks/useIsRuleEditable'; diff --git a/public/app/features/alerting/unified/components/rules/RulesGroup.tsx b/public/app/features/alerting/unified/components/rules/RulesGroup.tsx index ccf274847b6..4fd7b7a91e5 100644 --- a/public/app/features/alerting/unified/components/rules/RulesGroup.tsx +++ b/public/app/features/alerting/unified/components/rules/RulesGroup.tsx @@ -1,10 +1,10 @@ import { css } from '@emotion/css'; import pluralize from 'pluralize'; import React, { FC, useEffect, useState } from 'react'; -import { useDispatch } from 'react-redux'; import { GrafanaTheme2 } from '@grafana/data'; import { Badge, ConfirmModal, HorizontalGroup, Icon, Spinner, Tooltip, useStyles2 } from '@grafana/ui'; +import { useDispatch } from 'app/types'; import { CombinedRuleGroup, CombinedRuleNamespace } from 'app/types/unified-alerting'; import { useFolder } from '../../hooks/useFolder'; diff --git a/public/app/features/alerting/unified/components/silences/MatchedSilencedRules.tsx b/public/app/features/alerting/unified/components/silences/MatchedSilencedRules.tsx index d28d9ece726..be00d696cc0 100644 --- a/public/app/features/alerting/unified/components/silences/MatchedSilencedRules.tsx +++ b/public/app/features/alerting/unified/components/silences/MatchedSilencedRules.tsx @@ -1,11 +1,11 @@ import { css } from '@emotion/css'; import React, { useEffect, useState } from 'react'; import { useFormContext } from 'react-hook-form'; -import { useDispatch } from 'react-redux'; import { useDebounce } from 'react-use'; import { dateTime, GrafanaTheme2 } from '@grafana/data'; import { Badge, useStyles2 } from '@grafana/ui'; +import { useDispatch } from 'app/types'; import { Alert, AlertingRule } from 'app/types/unified-alerting'; import { useCombinedRuleNamespaces } from '../../hooks/useCombinedRuleNamespaces'; diff --git a/public/app/features/alerting/unified/components/silences/SilenceTableRow.tsx b/public/app/features/alerting/unified/components/silences/SilenceTableRow.tsx index 1897361de84..e21e6fe8ba4 100644 --- a/public/app/features/alerting/unified/components/silences/SilenceTableRow.tsx +++ b/public/app/features/alerting/unified/components/silences/SilenceTableRow.tsx @@ -1,11 +1,11 @@ import { css, cx } from '@emotion/css'; import React, { FC, Fragment, useState } from 'react'; -import { useDispatch } from 'react-redux'; import { dateMath, GrafanaTheme, intervalToAbbreviatedDurationString } from '@grafana/data'; import { useStyles, Link } from '@grafana/ui'; import { contextSrv } from 'app/core/services/context_srv'; import { Silence, AlertmanagerAlert } from 'app/plugins/datasource/alertmanager/types'; +import { useDispatch } from 'app/types'; import { expireSilenceAction } from '../../state/actions'; import { makeAMLink } from '../../utils/misc'; diff --git a/public/app/features/alerting/unified/components/silences/SilencesEditor.tsx b/public/app/features/alerting/unified/components/silences/SilencesEditor.tsx index 79bcbcc88c3..52fa9d54bdc 100644 --- a/public/app/features/alerting/unified/components/silences/SilencesEditor.tsx +++ b/public/app/features/alerting/unified/components/silences/SilencesEditor.tsx @@ -2,7 +2,6 @@ import { css, cx } from '@emotion/css'; import { pickBy } from 'lodash'; import React, { FC, useMemo, useState } from 'react'; import { useForm, FormProvider } from 'react-hook-form'; -import { useDispatch } from 'react-redux'; import { useDebounce } from 'react-use'; import { @@ -18,6 +17,7 @@ import { config } from '@grafana/runtime'; import { Button, Field, FieldSet, Input, LinkButton, TextArea, useStyles2 } from '@grafana/ui'; import { useCleanup } from 'app/core/hooks/useCleanup'; import { MatcherOperator, Silence, SilenceCreatePayload } from 'app/plugins/datasource/alertmanager/types'; +import { useDispatch } from 'app/types'; import { useURLSearchParams } from '../../hooks/useURLSearchParams'; import { useUnifiedAlertingSelector } from '../../hooks/useUnifiedAlertingSelector'; diff --git a/public/app/features/alerting/unified/components/silences/SilencesTable.tsx b/public/app/features/alerting/unified/components/silences/SilencesTable.tsx index ee250f116b5..ef021d61fe9 100644 --- a/public/app/features/alerting/unified/components/silences/SilencesTable.tsx +++ b/public/app/features/alerting/unified/components/silences/SilencesTable.tsx @@ -1,12 +1,12 @@ import { css } from '@emotion/css'; import React, { FC, useMemo } from 'react'; -import { useDispatch } from 'react-redux'; import { GrafanaTheme2, dateMath } from '@grafana/data'; import { Icon, useStyles2, Link, Button, Stack } from '@grafana/ui'; import { useQueryParams } from 'app/core/hooks/useQueryParams'; import { contextSrv } from 'app/core/services/context_srv'; import { AlertmanagerAlert, Silence, SilenceState } from 'app/plugins/datasource/alertmanager/types'; +import { useDispatch } from 'app/types'; import { expireSilenceAction } from '../../state/actions'; import { getInstancesPermissions } from '../../utils/access-control'; diff --git a/public/app/features/alerting/unified/hooks/useCombinedRule.ts b/public/app/features/alerting/unified/hooks/useCombinedRule.ts index 55d853abcc2..393cca5c80d 100644 --- a/public/app/features/alerting/unified/hooks/useCombinedRule.ts +++ b/public/app/features/alerting/unified/hooks/useCombinedRule.ts @@ -1,7 +1,7 @@ import { useMemo } from 'react'; -import { useDispatch } from 'react-redux'; import { useAsync } from 'react-use'; +import { useDispatch } from 'app/types'; import { CombinedRule, RuleIdentifier, RuleNamespace } from 'app/types/unified-alerting'; import { RulerRulesConfigDTO } from 'app/types/unified-alerting-dto'; diff --git a/public/app/features/alerting/unified/hooks/useExternalAMSelector.test.tsx b/public/app/features/alerting/unified/hooks/useExternalAMSelector.test.tsx index 102560fa9e0..f829676380a 100644 --- a/public/app/features/alerting/unified/hooks/useExternalAMSelector.test.tsx +++ b/public/app/features/alerting/unified/hooks/useExternalAMSelector.test.tsx @@ -1,6 +1,6 @@ import { renderHook } from '@testing-library/react-hooks'; import React from 'react'; -import * as reactRedux from 'react-redux'; +import { Provider } from 'react-redux'; import { DataSourceJsonData, DataSourceSettings } from '@grafana/data'; import { config } from '@grafana/runtime'; @@ -10,17 +10,12 @@ import { mockDataSource, mockDataSourcesStore, mockStore } from '../mocks'; import { useExternalAmSelector, useExternalDataSourceAlertmanagers } from './useExternalAmSelector'; -const useSelectorMock = jest.spyOn(reactRedux, 'useSelector'); - describe('useExternalAmSelector', () => { - beforeEach(() => { - useSelectorMock.mockClear(); - }); it('should have one in pending', () => { - useSelectorMock.mockImplementation((callback) => { - return callback(createMockStoreState([], [], ['some/url/to/am'])); - }); - const alertmanagers = useExternalAmSelector(); + const store = createMockStoreState([], [], ['some/url/to/am']); + const wrapper = ({ children }: React.PropsWithChildren<{}>) => {children}; + const { result } = renderHook(() => useExternalAmSelector(), { wrapper }); + const alertmanagers = result.current; expect(alertmanagers).toEqual([ { @@ -32,13 +27,14 @@ describe('useExternalAmSelector', () => { }); it('should have one active, one pending', () => { - useSelectorMock.mockImplementation((callback) => { - return callback( - createMockStoreState([{ url: 'some/url/to/am/api/v2/alerts' }], [], ['some/url/to/am', 'some/url/to/am1']) - ); - }); - - const alertmanagers = useExternalAmSelector(); + const store = createMockStoreState( + [{ url: 'some/url/to/am/api/v2/alerts' }], + [], + ['some/url/to/am', 'some/url/to/am1'] + ); + const wrapper = ({ children }: React.PropsWithChildren<{}>) => {children}; + const { result } = renderHook(() => useExternalAmSelector(), { wrapper }); + const alertmanagers = result.current; expect(alertmanagers).toEqual([ { @@ -55,17 +51,14 @@ describe('useExternalAmSelector', () => { }); it('should have two active', () => { - useSelectorMock.mockImplementation((callback) => { - return callback( - createMockStoreState( - [{ url: 'some/url/to/am/api/v2/alerts' }, { url: 'some/url/to/am1/api/v2/alerts' }], - [], - ['some/url/to/am', 'some/url/to/am1'] - ) - ); - }); - - const alertmanagers = useExternalAmSelector(); + const store = createMockStoreState( + [{ url: 'some/url/to/am/api/v2/alerts' }, { url: 'some/url/to/am1/api/v2/alerts' }], + [], + ['some/url/to/am', 'some/url/to/am1'] + ); + const wrapper = ({ children }: React.PropsWithChildren<{}>) => {children}; + const { result } = renderHook(() => useExternalAmSelector(), { wrapper }); + const alertmanagers = result.current; expect(alertmanagers).toEqual([ { @@ -82,18 +75,15 @@ describe('useExternalAmSelector', () => { }); it('should have one active, one dropped, one pending', () => { - useSelectorMock.mockImplementation((callback) => { - return callback( - createMockStoreState( - [{ url: 'some/url/to/am/api/v2/alerts' }], - [{ url: 'some/dropped/url/api/v2/alerts' }], - ['some/url/to/am', 'some/url/to/am1'] - ) - ); - }); - - const alertmanagers = useExternalAmSelector(); + const store = createMockStoreState( + [{ url: 'some/url/to/am/api/v2/alerts' }], + [{ url: 'some/dropped/url/api/v2/alerts' }], + ['some/url/to/am', 'some/url/to/am1'] + ); + const wrapper = ({ children }: React.PropsWithChildren<{}>) => {children}; + const { result } = renderHook(() => useExternalAmSelector(), { wrapper }); + const alertmanagers = result.current; expect(alertmanagers).toEqual([ { url: 'some/url/to/am', @@ -114,21 +104,19 @@ describe('useExternalAmSelector', () => { }); it('The number of alert managers should match config entries when there are multiple entries of the same url', () => { - useSelectorMock.mockImplementation((callback) => { - return callback( - createMockStoreState( - [ - { url: 'same/url/to/am/api/v2/alerts' }, - { url: 'same/url/to/am/api/v2/alerts' }, - { url: 'same/url/to/am/api/v2/alerts' }, - ], - [], - ['same/url/to/am', 'same/url/to/am', 'same/url/to/am'] - ) - ); - }); + const store = createMockStoreState( + [ + { url: 'same/url/to/am/api/v2/alerts' }, + { url: 'same/url/to/am/api/v2/alerts' }, + { url: 'same/url/to/am/api/v2/alerts' }, + ], + [], + ['same/url/to/am', 'same/url/to/am', 'same/url/to/am'] + ); + const wrapper = ({ children }: React.PropsWithChildren<{}>) => {children}; - const alertmanagers = useExternalAmSelector(); + const { result } = renderHook(() => useExternalAmSelector(), { wrapper }); + const alertmanagers = result.current; expect(alertmanagers.length).toBe(3); expect(alertmanagers).toEqual([ @@ -152,10 +140,6 @@ describe('useExternalAmSelector', () => { }); describe('useExternalDataSourceAlertmanagers', () => { - beforeEach(() => { - useSelectorMock.mockRestore(); - }); - it('Should merge data sources information from config and api responses', () => { // Arrange const { dsSettings, dsInstanceSettings } = setupAlertmanagerDataSource({ url: 'http://grafana.com' }); @@ -168,7 +152,7 @@ describe('useExternalDataSourceAlertmanagers', () => { dataSources: [dsSettings], }); - const wrapper: React.FC = ({ children }) => {children}; + const wrapper: React.FC = ({ children }) => {children}; // Act const { @@ -199,7 +183,7 @@ describe('useExternalDataSourceAlertmanagers', () => { }; }); - const wrapper: React.FC = ({ children }) => {children}; + const wrapper: React.FC = ({ children }) => {children}; // Act const { @@ -230,7 +214,7 @@ describe('useExternalDataSourceAlertmanagers', () => { }; }); - const wrapper: React.FC = ({ children }) => {children}; + const wrapper: React.FC = ({ children }) => {children}; // Act const { @@ -261,7 +245,7 @@ describe('useExternalDataSourceAlertmanagers', () => { }; }); - const wrapper: React.FC = ({ children }) => {children}; + const wrapper: React.FC = ({ children }) => {children}; // Act const { @@ -292,7 +276,7 @@ describe('useExternalDataSourceAlertmanagers', () => { }; }); - const wrapper: React.FC = ({ children }) => {children}; + const wrapper: React.FC = ({ children }) => {children}; // Act const { @@ -326,7 +310,7 @@ describe('useExternalDataSourceAlertmanagers', () => { }; }); - const wrapper: React.FC = ({ children }) => {children}; + const wrapper: React.FC = ({ children }) => {children}; // Act const { @@ -389,28 +373,16 @@ const createMockStoreState = ( droppedAlertmanagers: Array<{ url: string }>, alertmanagerConfig: string[] ) => { - return { - unifiedAlerting: { - externalAlertmanagers: { - discoveredAlertmanagers: { - result: { - data: { - activeAlertManagers: activeAlertmanagers, - droppedAlertManagers: droppedAlertmanagers, - }, - }, - dispatched: false, - loading: false, - }, - alertmanagerConfig: { - result: { - alertmanagers: alertmanagerConfig, - alertmanagersChoice: AlertmanagerChoice.All, - }, - dispatched: false, - loading: false, - }, + return mockStore((state) => { + state.unifiedAlerting.externalAlertmanagers.alertmanagerConfig.result = { + alertmanagers: alertmanagerConfig, + alertmanagersChoice: AlertmanagerChoice.All, + }; + state.unifiedAlerting.externalAlertmanagers.discoveredAlertmanagers.result = { + data: { + activeAlertManagers: activeAlertmanagers, + droppedAlertManagers: droppedAlertmanagers, }, - }, - }; + }; + }); }; diff --git a/public/app/features/alerting/unified/hooks/useExternalAmSelector.ts b/public/app/features/alerting/unified/hooks/useExternalAmSelector.ts index b4a546ba919..38147df45bf 100644 --- a/public/app/features/alerting/unified/hooks/useExternalAmSelector.ts +++ b/public/app/features/alerting/unified/hooks/useExternalAmSelector.ts @@ -1,10 +1,9 @@ import { countBy, keyBy } from 'lodash'; -import { useSelector } from 'react-redux'; import { DataSourceInstanceSettings, DataSourceJsonData, DataSourceSettings } from '@grafana/data'; import { AlertManagerDataSourceJsonData } from 'app/plugins/datasource/alertmanager/types'; +import { useSelector } from 'app/types'; -import { StoreState } from '../../../../types'; import { getAlertManagerDataSources } from '../utils/datasource'; import { useUnifiedAlertingSelector } from './useUnifiedAlertingSelector'; @@ -14,10 +13,10 @@ type AlertmanagerConfig = { url: string; status: string; actualUrl: string }; export function useExternalAmSelector(): AlertmanagerConfig[] | [] { const discoveredAlertmanagers = useSelector( - (state: StoreState) => state.unifiedAlerting.externalAlertmanagers.discoveredAlertmanagers.result?.data + (state) => state.unifiedAlerting.externalAlertmanagers.discoveredAlertmanagers.result?.data ); const alertmanagerConfig = useSelector( - (state: StoreState) => state.unifiedAlerting.externalAlertmanagers.alertmanagerConfig.result?.alertmanagers + (state) => state.unifiedAlerting.externalAlertmanagers.alertmanagerConfig.result?.alertmanagers ); if (!discoveredAlertmanagers || !alertmanagerConfig) { @@ -69,7 +68,7 @@ export interface ExternalDataSourceAM { export function useExternalDataSourceAlertmanagers(): ExternalDataSourceAM[] { const externalDsAlertManagers = getAlertManagerDataSources().filter((ds) => ds.jsonData.handleGrafanaManagedAlerts); - const alertmanagerDatasources = useSelector((state: StoreState) => + const alertmanagerDatasources = useSelector((state) => keyBy( state.dataSources.dataSources.filter((ds) => ds.type === 'alertmanager'), (ds) => ds.uid diff --git a/public/app/features/alerting/unified/hooks/useFolder.ts b/public/app/features/alerting/unified/hooks/useFolder.ts index a02710b026b..c627453445b 100644 --- a/public/app/features/alerting/unified/hooks/useFolder.ts +++ b/public/app/features/alerting/unified/hooks/useFolder.ts @@ -1,7 +1,6 @@ import { useEffect } from 'react'; -import { useDispatch } from 'react-redux'; -import { FolderDTO } from 'app/types'; +import { FolderDTO, useDispatch } from 'app/types'; import { fetchFolderIfNotFetchedAction } from '../state/actions'; import { initialAsyncRequestState } from '../utils/redux'; diff --git a/public/app/features/alerting/unified/hooks/useManagedAlertStateHistory.ts b/public/app/features/alerting/unified/hooks/useManagedAlertStateHistory.ts index 115c623a881..43b10d666f5 100644 --- a/public/app/features/alerting/unified/hooks/useManagedAlertStateHistory.ts +++ b/public/app/features/alerting/unified/hooks/useManagedAlertStateHistory.ts @@ -1,6 +1,6 @@ import { useEffect } from 'react'; -import { useDispatch } from 'react-redux'; +import { useDispatch } from 'app/types'; import { StateHistoryItem } from 'app/types/unified-alerting'; import { fetchGrafanaAnnotationsAction } from '../state/actions'; diff --git a/public/app/features/alerting/unified/hooks/usePanelCombinedRules.ts b/public/app/features/alerting/unified/hooks/usePanelCombinedRules.ts index f7d562bd647..a0a88f1d83e 100644 --- a/public/app/features/alerting/unified/hooks/usePanelCombinedRules.ts +++ b/public/app/features/alerting/unified/hooks/usePanelCombinedRules.ts @@ -1,8 +1,8 @@ import { SerializedError } from '@reduxjs/toolkit'; import { useEffect, useMemo } from 'react'; -import { useDispatch } from 'react-redux'; import { DashboardModel, PanelModel } from 'app/features/dashboard/state'; +import { useDispatch } from 'app/types'; import { CombinedRule } from 'app/types/unified-alerting'; import { fetchPromRulesAction, fetchRulerRulesAction } from '../state/actions'; diff --git a/public/app/features/alerting/unified/hooks/useUnifiedAlertingSelector.ts b/public/app/features/alerting/unified/hooks/useUnifiedAlertingSelector.ts index 57b335ebb47..34d6e3aa086 100644 --- a/public/app/features/alerting/unified/hooks/useUnifiedAlertingSelector.ts +++ b/public/app/features/alerting/unified/hooks/useUnifiedAlertingSelector.ts @@ -1,6 +1,4 @@ -import { useSelector } from 'react-redux'; - -import { StoreState } from 'app/types'; +import { useSelector } from 'app/types'; import { UnifiedAlertingState } from '../state/reducers'; @@ -8,5 +6,5 @@ export function useUnifiedAlertingSelector( selector: (state: UnifiedAlertingState) => TSelected, equalityFn?: (left: TSelected, right: TSelected) => boolean ): TSelected { - return useSelector((state: StoreState) => selector(state.unifiedAlerting), equalityFn); + return useSelector((state) => selector(state.unifiedAlerting), equalityFn); } diff --git a/public/app/features/commandPalette/CommandPalette.tsx b/public/app/features/commandPalette/CommandPalette.tsx index 839a04733ec..8aa10de32b6 100644 --- a/public/app/features/commandPalette/CommandPalette.tsx +++ b/public/app/features/commandPalette/CommandPalette.tsx @@ -13,13 +13,12 @@ import { useKBar, } from 'kbar'; import React, { useEffect, useState } from 'react'; -import { useSelector } from 'react-redux'; import { GrafanaTheme2 } from '@grafana/data'; import { reportInteraction, locationService } from '@grafana/runtime'; import { useStyles2 } from '@grafana/ui'; import { useGrafana } from 'app/core/context/GrafanaContext'; -import { StoreState } from 'app/types'; +import { useSelector } from 'app/types'; import { ResultItem } from './ResultItem'; import getDashboardNavActions from './actions/dashboard.nav.actions'; @@ -40,7 +39,7 @@ export const CommandPalette = () => { })); const isNotLogin = locationService.getLocation().pathname !== '/login'; - const { navBarTree } = useSelector((state: StoreState) => { + const { navBarTree } = useSelector((state) => { return { navBarTree: state.navBarTree, }; diff --git a/public/app/features/dashboard/components/DashboardPrompt/DashboardPrompt.tsx b/public/app/features/dashboard/components/DashboardPrompt/DashboardPrompt.tsx index 7f19ae22ccb..14024ccfaa4 100644 --- a/public/app/features/dashboard/components/DashboardPrompt/DashboardPrompt.tsx +++ b/public/app/features/dashboard/components/DashboardPrompt/DashboardPrompt.tsx @@ -1,7 +1,6 @@ import * as H from 'history'; import { each, find } from 'lodash'; import React, { useContext, useEffect, useState } from 'react'; -import { useDispatch } from 'react-redux'; import { Prompt } from 'react-router-dom'; import { locationService } from '@grafana/runtime'; @@ -10,6 +9,7 @@ import { appEvents } from 'app/core/app_events'; import { contextSrv } from 'app/core/services/context_srv'; import { SaveLibraryPanelModal } from 'app/features/library-panels/components/SaveLibraryPanelModal/SaveLibraryPanelModal'; import { PanelModelWithLibraryPanel } from 'app/features/library-panels/types'; +import { useDispatch } from 'app/types'; import { DashboardSavedEvent } from 'app/types/events'; import { DashboardModel } from '../../state/DashboardModel'; diff --git a/public/app/features/dashboard/components/PanelEditor/OptionsPane.tsx b/public/app/features/dashboard/components/PanelEditor/OptionsPane.tsx index 2a025affce4..793d3896860 100644 --- a/public/app/features/dashboard/components/PanelEditor/OptionsPane.tsx +++ b/public/app/features/dashboard/components/PanelEditor/OptionsPane.tsx @@ -1,11 +1,10 @@ import { css } from '@emotion/css'; import React from 'react'; -import { useSelector } from 'react-redux'; import { GrafanaTheme } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { useStyles } from '@grafana/ui'; -import { StoreState } from 'app/types'; +import { useSelector } from 'app/types'; import { OptionsPaneOptions } from './OptionsPaneOptions'; import { VisualizationButton } from './VisualizationButton'; @@ -23,7 +22,7 @@ export const OptionsPane: React.FC = ({ instanceState, }) => { const styles = useStyles(getStyles); - const isVizPickerOpen = useSelector((state: StoreState) => state.panelEditor.isVizPickerOpen); + const isVizPickerOpen = useSelector((state) => state.panelEditor.isVizPickerOpen); const { data } = usePanelLatestData(panel, { withTransforms: true, withFieldConfig: false }, true); return ( diff --git a/public/app/features/dashboard/components/PanelEditor/VisualizationButton.tsx b/public/app/features/dashboard/components/PanelEditor/VisualizationButton.tsx index c0f5ba704ac..2d0b470b66f 100644 --- a/public/app/features/dashboard/components/PanelEditor/VisualizationButton.tsx +++ b/public/app/features/dashboard/components/PanelEditor/VisualizationButton.tsx @@ -1,11 +1,10 @@ import { css } from '@emotion/css'; import React, { FC } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; import { GrafanaTheme } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { ToolbarButton, ButtonGroup, useStyles } from '@grafana/ui'; -import { StoreState } from 'app/types'; +import { useDispatch, useSelector } from 'app/types'; import { PanelModel } from '../../state'; import { getPanelPluginWithFallback } from '../../state/selectors'; @@ -20,8 +19,8 @@ export const VisualizationButton: FC = ({ panel }) => { const styles = useStyles(getStyles); const dispatch = useDispatch(); const plugin = useSelector(getPanelPluginWithFallback(panel.type)); - const isPanelOptionsVisible = useSelector((state: StoreState) => state.panelEditor.ui.isPanelOptionsVisible); - const isVizPickerOpen = useSelector((state: StoreState) => state.panelEditor.isVizPickerOpen); + const isPanelOptionsVisible = useSelector((state) => state.panelEditor.ui.isPanelOptionsVisible); + const isVizPickerOpen = useSelector((state) => state.panelEditor.isVizPickerOpen); const onToggleOpen = () => { dispatch(toggleVizPicker(!isVizPickerOpen)); diff --git a/public/app/features/dashboard/components/PanelEditor/VisualizationSelectPane.tsx b/public/app/features/dashboard/components/PanelEditor/VisualizationSelectPane.tsx index f18f75e6f8a..50726843ebe 100644 --- a/public/app/features/dashboard/components/PanelEditor/VisualizationSelectPane.tsx +++ b/public/app/features/dashboard/components/PanelEditor/VisualizationSelectPane.tsx @@ -1,6 +1,5 @@ import { css } from '@emotion/css'; import React, { FC, useCallback, useRef, useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; import { useLocalStorage } from 'react-use'; import { GrafanaTheme, PanelData, SelectableValue } from '@grafana/data'; @@ -11,6 +10,7 @@ import { LS_VISUALIZATION_SELECT_TAB_KEY } from 'app/core/constants'; import { PanelLibraryOptionsGroup } from 'app/features/library-panels/components/PanelLibraryOptionsGroup/PanelLibraryOptionsGroup'; import { VisualizationSuggestions } from 'app/features/panel/components/VizTypePicker/VisualizationSuggestions'; import { VizTypeChangeDetails } from 'app/features/panel/components/VizTypePicker/types'; +import { useDispatch, useSelector } from 'app/types'; import { VizTypePicker } from '../../../panel/components/VizTypePicker/VizTypePicker'; import { changePanelPlugin } from '../../../panel/state/actions'; diff --git a/public/app/features/dashboard/components/RepeatRowSelect/RepeatRowSelect.tsx b/public/app/features/dashboard/components/RepeatRowSelect/RepeatRowSelect.tsx index 346840c1bf1..208732b6508 100644 --- a/public/app/features/dashboard/components/RepeatRowSelect/RepeatRowSelect.tsx +++ b/public/app/features/dashboard/components/RepeatRowSelect/RepeatRowSelect.tsx @@ -1,10 +1,9 @@ import React, { FC, useCallback, useMemo } from 'react'; -import { useSelector } from 'react-redux'; import { SelectableValue } from '@grafana/data'; import { Select } from '@grafana/ui'; +import { useSelector } from 'app/types'; -import { StoreState } from '../../../../types'; import { getLastKey, getVariablesByKey } from '../../../variables/state/selectors'; export interface Props { @@ -14,7 +13,7 @@ export interface Props { } export const RepeatRowSelect: FC = ({ repeat, onChange, id }) => { - const variables = useSelector((state: StoreState) => { + const variables = useSelector((state) => { return getVariablesByKey(getLastKey(state), state); }); diff --git a/public/app/features/dashboard/components/SaveDashboard/useDashboardSave.tsx b/public/app/features/dashboard/components/SaveDashboard/useDashboardSave.tsx index 89c349cf0c3..d9713bc7e41 100644 --- a/public/app/features/dashboard/components/SaveDashboard/useDashboardSave.tsx +++ b/public/app/features/dashboard/components/SaveDashboard/useDashboardSave.tsx @@ -1,5 +1,4 @@ import { useEffect } from 'react'; -import { useDispatch } from 'react-redux'; import useAsyncFn from 'react-use/lib/useAsyncFn'; import { locationUtil } from '@grafana/data'; @@ -10,6 +9,7 @@ import { contextSrv } from 'app/core/core'; import { updateDashboardName } from 'app/core/reducers/navBarTree'; import { DashboardModel } from 'app/features/dashboard/state'; import { saveDashboard as saveDashboardApiCall } from 'app/features/manage-dashboards/state/actions'; +import { useDispatch } from 'app/types'; import { DashboardSavedEvent } from 'app/types/events'; import { SaveDashboardOptions } from './types'; diff --git a/public/app/features/dashboard/components/VersionHistory/useDashboardRestore.tsx b/public/app/features/dashboard/components/VersionHistory/useDashboardRestore.tsx index b54c2d017fb..fb9e3a70f34 100644 --- a/public/app/features/dashboard/components/VersionHistory/useDashboardRestore.tsx +++ b/public/app/features/dashboard/components/VersionHistory/useDashboardRestore.tsx @@ -1,11 +1,10 @@ import { useEffect } from 'react'; -import { useSelector } from 'react-redux'; import { useAsyncFn } from 'react-use'; import { locationUtil } from '@grafana/data'; import { locationService } from '@grafana/runtime'; import { useAppNotification } from 'app/core/copy/appNotification'; -import { StoreState } from 'app/types'; +import { useSelector } from 'app/types'; import { DashboardModel } from '../../state'; @@ -16,7 +15,7 @@ const restoreDashboard = async (version: number, dashboard: DashboardModel) => { }; export const useDashboardRestore = (version: number) => { - const dashboard = useSelector((state: StoreState) => state.dashboard.getModel()); + const dashboard = useSelector((state) => state.dashboard.getModel()); const [state, onRestoreDashboard] = useAsyncFn(async () => await restoreDashboard(version, dashboard!), []); const notifyApp = useAppNotification(); diff --git a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenuProvider.tsx b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenuProvider.tsx index 54088922bf1..e3333ed432f 100644 --- a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenuProvider.tsx +++ b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenuProvider.tsx @@ -1,10 +1,9 @@ import { FC, ReactElement, useEffect, useState } from 'react'; -import { useSelector } from 'react-redux'; import { PanelMenuItem } from '@grafana/data'; import { getPanelStateForModel } from 'app/features/panel/state/selectors'; +import { useSelector } from 'app/types'; -import { StoreState } from '../../../../types'; import { DashboardModel, PanelModel } from '../../state'; import { getPanelMenu } from '../../utils/getPanelMenu'; @@ -20,7 +19,7 @@ interface Props { export const PanelHeaderMenuProvider: FC = ({ panel, dashboard, children }) => { const [items, setItems] = useState([]); - const angularComponent = useSelector((state: StoreState) => getPanelStateForModel(state, panel)?.angularComponent); + const angularComponent = useSelector((state) => getPanelStateForModel(state, panel)?.angularComponent); useEffect(() => { setItems(getPanelMenu(dashboard, panel, angularComponent)); diff --git a/public/app/features/data-connections/hooks/useNavModel.ts b/public/app/features/data-connections/hooks/useNavModel.ts index 36ad82fcdde..f3aa6be0a9d 100644 --- a/public/app/features/data-connections/hooks/useNavModel.ts +++ b/public/app/features/data-connections/hooks/useNavModel.ts @@ -1,8 +1,7 @@ -import { useSelector } from 'react-redux'; import { useLocation } from 'react-router-dom'; import { NavModelItem } from '@grafana/data'; -import { StoreState } from 'app/types/store'; +import { useSelector } from 'app/types'; import { ROUTE_BASE_ID } from '../constants'; @@ -11,7 +10,7 @@ import { ROUTE_BASE_ID } from '../constants'; // the child nav-model-item's ID on the call-site.) export const useNavModel = () => { const { pathname: currentPath } = useLocation(); - const navIndex = useSelector((state: StoreState) => state.navIndex); + const navIndex = useSelector((state) => state.navIndex); const node = navIndex[ROUTE_BASE_ID]; const main = node; const isDefaultRoute = (item: NavModelItem) => diff --git a/public/app/features/datasources/components/DataSourceDashboards.tsx b/public/app/features/datasources/components/DataSourceDashboards.tsx index f419b1684bd..afc5aff0457 100644 --- a/public/app/features/datasources/components/DataSourceDashboards.tsx +++ b/public/app/features/datasources/components/DataSourceDashboards.tsx @@ -1,10 +1,9 @@ import React, { useEffect } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; import PageLoader from 'app/core/components/PageLoader/PageLoader'; import { importDashboard, removeDashboard } from 'app/features/dashboard/state/actions'; import { loadPluginDashboards } from 'app/features/plugins/admin/state/actions'; -import { PluginDashboard, StoreState } from 'app/types'; +import { PluginDashboard, StoreState, useDispatch, useSelector } from 'app/types'; import DashboardTable from '../components/DashboardsTable'; import { useLoadDataSource } from '../state'; diff --git a/public/app/features/datasources/components/DataSourcesList.tsx b/public/app/features/datasources/components/DataSourcesList.tsx index a3abb7e00ad..608e93caec0 100644 --- a/public/app/features/datasources/components/DataSourcesList.tsx +++ b/public/app/features/datasources/components/DataSourcesList.tsx @@ -1,13 +1,12 @@ import { css } from '@emotion/css'; import React from 'react'; -import { useSelector } from 'react-redux'; import { DataSourceSettings } from '@grafana/data'; import { Card, Tag, useStyles } from '@grafana/ui'; import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; import PageLoader from 'app/core/components/PageLoader/PageLoader'; import { contextSrv } from 'app/core/core'; -import { StoreState, AccessControlAction } from 'app/types'; +import { StoreState, AccessControlAction, useSelector } from 'app/types'; import { getDataSources, getDataSourcesCount, useDataSourcesRoutes, useLoadDataSources } from '../state'; @@ -16,7 +15,7 @@ import { DataSourcesListHeader } from './DataSourcesListHeader'; export function DataSourcesList() { useLoadDataSources(); - const dataSources = useSelector((state: StoreState) => getDataSources(state.dataSources)); + const dataSources = useSelector((state) => getDataSources(state.dataSources)); const dataSourcesCount = useSelector(({ dataSources }: StoreState) => getDataSourcesCount(dataSources)); const hasFetched = useSelector(({ dataSources }: StoreState) => dataSources.hasFetched); const hasCreateRights = contextSrv.hasPermission(AccessControlAction.DataSourcesCreate); diff --git a/public/app/features/datasources/components/DataSourcesListHeader.tsx b/public/app/features/datasources/components/DataSourcesListHeader.tsx index 8ce4a972e6c..8db867bb245 100644 --- a/public/app/features/datasources/components/DataSourcesListHeader.tsx +++ b/public/app/features/datasources/components/DataSourcesListHeader.tsx @@ -1,10 +1,9 @@ import React, { useCallback } from 'react'; -import { useSelector, useDispatch } from 'react-redux'; import { AnyAction } from 'redux'; import PageActionBar from 'app/core/components/PageActionBar/PageActionBar'; import { contextSrv } from 'app/core/core'; -import { AccessControlAction, StoreState } from 'app/types'; +import { AccessControlAction, StoreState, useSelector, useDispatch } from 'app/types'; import { getDataSourcesSearchQuery, setDataSourcesSearchQuery, useDataSourcesRoutes } from '../state'; diff --git a/public/app/features/datasources/components/EditDataSource.tsx b/public/app/features/datasources/components/EditDataSource.tsx index bd5da825deb..f5e8eacc621 100644 --- a/public/app/features/datasources/components/EditDataSource.tsx +++ b/public/app/features/datasources/components/EditDataSource.tsx @@ -1,10 +1,9 @@ import { AnyAction } from '@reduxjs/toolkit'; import React from 'react'; -import { useDispatch } from 'react-redux'; import { DataSourcePluginMeta, DataSourceSettings as DataSourceSettingsType } from '@grafana/data'; import PageLoader from 'app/core/components/PageLoader/PageLoader'; -import { DataSourceSettingsState, ThunkResult } from 'app/types'; +import { DataSourceSettingsState, useDispatch } from 'app/types'; import { dataSourceLoaded, @@ -85,8 +84,8 @@ export type ViewProps = { onDefaultChange: (isDefault: boolean) => AnyAction; onNameChange: (name: string) => AnyAction; onOptionsChange: (dataSource: DataSourceSettingsType) => AnyAction; - onTest: () => ThunkResult; - onUpdate: (dataSource: DataSourceSettingsType) => ThunkResult; + onTest: () => void; + onUpdate: (dataSource: DataSourceSettingsType) => Promise; }; export function EditDataSourceView({ diff --git a/public/app/features/datasources/components/NewDataSource.tsx b/public/app/features/datasources/components/NewDataSource.tsx index af47fe1895b..6b12bb3fc52 100644 --- a/public/app/features/datasources/components/NewDataSource.tsx +++ b/public/app/features/datasources/components/NewDataSource.tsx @@ -1,12 +1,11 @@ import React from 'react'; -import { useDispatch, useSelector } from 'react-redux'; import { AnyAction } from 'redux'; import { DataSourcePluginMeta } from '@grafana/data'; import { LinkButton, FilterInput } from '@grafana/ui'; import PageLoader from 'app/core/components/PageLoader/PageLoader'; import { PluginsErrorsInfo } from 'app/features/plugins/components/PluginsErrorsInfo'; -import { DataSourcePluginCategory, StoreState } from 'app/types'; +import { DataSourcePluginCategory, StoreState, useDispatch, useSelector } from 'app/types'; import { DataSourceCategories } from '../components/DataSourceCategories'; import { DataSourceTypeCardList } from '../components/DataSourceTypeCardList'; diff --git a/public/app/features/datasources/state/hooks.ts b/public/app/features/datasources/state/hooks.ts index f6d05c52160..cc6bc00c054 100644 --- a/public/app/features/datasources/state/hooks.ts +++ b/public/app/features/datasources/state/hooks.ts @@ -1,12 +1,11 @@ import { useContext, useEffect } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; import { DataSourcePluginMeta, DataSourceSettings, NavModelItem, urlUtil } from '@grafana/data'; import { cleanUpAction } from 'app/core/actions/cleanUp'; import appEvents from 'app/core/app_events'; import { contextSrv } from 'app/core/core'; import { getNavModel } from 'app/core/selectors/navModel'; -import { AccessControlAction, StoreState } from 'app/types'; +import { AccessControlAction, useDispatch, useSelector } from 'app/types'; import { ShowConfirmModalEvent } from 'app/types/events'; import { DataSourceRights } from '../types'; @@ -83,12 +82,12 @@ export const useAddDatasource = () => { export const useUpdateDatasource = () => { const dispatch = useDispatch(); - return (dataSource: DataSourceSettings) => dispatch(updateDataSource(dataSource)); + return async (dataSource: DataSourceSettings) => dispatch(updateDataSource(dataSource)); }; export const useDeleteLoadedDataSource = () => { const dispatch = useDispatch(); - const { name } = useSelector((state: StoreState) => state.dataSources.dataSource); + const { name } = useSelector((state) => state.dataSources.dataSource); return () => { appEvents.publish( @@ -104,7 +103,7 @@ export const useDeleteLoadedDataSource = () => { }; export const useDataSource = (uid: string) => { - return useSelector((state: StoreState) => getDataSource(state.dataSources, uid)); + return useSelector((state) => getDataSource(state.dataSources, uid)); }; export const useDataSourceExploreUrl = (uid: string) => { @@ -116,17 +115,17 @@ export const useDataSourceExploreUrl = (uid: string) => { }; export const useDataSourceMeta = (pluginType: string): DataSourcePluginMeta => { - return useSelector((state: StoreState) => getDataSourceMeta(state.dataSources, pluginType)); + return useSelector((state) => getDataSourceMeta(state.dataSources, pluginType)); }; export const useDataSourceSettings = () => { - return useSelector((state: StoreState) => state.dataSourceSettings); + return useSelector((state) => state.dataSourceSettings); }; export const useDataSourceSettingsNav = (dataSourceId: string, pageId: string | null) => { const dataSource = useDataSource(dataSourceId); const { plugin, loadError, loading } = useDataSourceSettings(); - const navIndex = useSelector((state: StoreState) => state.navIndex); + const navIndex = useSelector((state) => state.navIndex); const navIndexId = pageId ? `datasource-${pageId}-${dataSourceId}` : `datasource-settings-${dataSourceId}`; if (loadError) { diff --git a/public/app/features/explore/AddToDashboard/AddToDashboardModal.tsx b/public/app/features/explore/AddToDashboard/AddToDashboardModal.tsx index 05d5f7263a2..34353afc939 100644 --- a/public/app/features/explore/AddToDashboard/AddToDashboardModal.tsx +++ b/public/app/features/explore/AddToDashboard/AddToDashboardModal.tsx @@ -1,7 +1,6 @@ import { partial } from 'lodash'; import React, { useEffect, useState } from 'react'; import { DeepMap, FieldError, useForm } from 'react-hook-form'; -import { useSelector } from 'react-redux'; import { locationUtil, SelectableValue } from '@grafana/data'; import { config, locationService, reportInteraction } from '@grafana/runtime'; @@ -9,7 +8,7 @@ import { Alert, Button, Field, InputControl, Modal, RadioButtonGroup } from '@gr import { DashboardPicker } from 'app/core/components/Select/DashboardPicker'; import { contextSrv } from 'app/core/services/context_srv'; import { removeDashboardToFetchFromLocalStorage } from 'app/features/dashboard/state/initDashboard'; -import { ExploreId, AccessControlAction } from 'app/types'; +import { ExploreId, AccessControlAction, useSelector } from 'app/types'; import { getExploreItemSelector } from '../state/selectors'; diff --git a/public/app/features/explore/AddToDashboard/index.tsx b/public/app/features/explore/AddToDashboard/index.tsx index 37cbd9a8697..cb36ecc8d57 100644 --- a/public/app/features/explore/AddToDashboard/index.tsx +++ b/public/app/features/explore/AddToDashboard/index.tsx @@ -1,8 +1,7 @@ import React, { useState } from 'react'; -import { useSelector } from 'react-redux'; import { ToolbarButton } from '@grafana/ui'; -import { ExploreId } from 'app/types'; +import { ExploreId, useSelector } from 'app/types'; import { getExploreItemSelector } from '../state/selectors'; diff --git a/public/app/features/explore/ExploreActions.tsx b/public/app/features/explore/ExploreActions.tsx index ce6848fc58c..f926f91ef1f 100644 --- a/public/app/features/explore/ExploreActions.tsx +++ b/public/app/features/explore/ExploreActions.tsx @@ -1,8 +1,7 @@ import { useRegisterActions, useKBar, Action, Priority } from 'kbar'; import { FC, useEffect, useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { ExploreId } from 'app/types'; +import { ExploreId, useDispatch, useSelector } from 'app/types'; import { splitOpen, splitClose } from './state/main'; import { runQueries } from './state/query'; diff --git a/public/app/features/explore/QueryRows.tsx b/public/app/features/explore/QueryRows.tsx index 772202c9349..33819182888 100644 --- a/public/app/features/explore/QueryRows.tsx +++ b/public/app/features/explore/QueryRows.tsx @@ -1,10 +1,10 @@ import { createSelector } from '@reduxjs/toolkit'; import React, { useCallback, useMemo } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; import { CoreApp, DataQuery, DataSourceInstanceSettings } from '@grafana/data'; import { getDataSourceSrv } from '@grafana/runtime'; import { getNextRefIdChar } from 'app/core/utils/query'; +import { useDispatch, useSelector } from 'app/types'; import { ExploreId } from 'app/types/explore'; import { getDatasourceSrv } from '../plugins/datasource_srv'; diff --git a/public/app/features/explore/ResponseErrorContainer.tsx b/public/app/features/explore/ResponseErrorContainer.tsx index 667187d2600..ec6f311892f 100644 --- a/public/app/features/explore/ResponseErrorContainer.tsx +++ b/public/app/features/explore/ResponseErrorContainer.tsx @@ -1,9 +1,9 @@ import React from 'react'; -import { useSelector } from 'react-redux'; import { LoadingState } from '@grafana/data'; +import { useSelector } from 'app/types'; -import { ExploreId, StoreState } from '../../types'; +import { ExploreId } from '../../types'; import { ErrorContainer } from './ErrorContainer'; @@ -11,7 +11,7 @@ interface Props { exploreId: ExploreId; } export function ResponseErrorContainer(props: Props) { - const queryResponse = useSelector((state: StoreState) => state.explore[props.exploreId]?.queryResponse); + const queryResponse = useSelector((state) => state.explore[props.exploreId]?.queryResponse); const queryError = queryResponse?.state === LoadingState.Error ? queryResponse?.error : undefined; // Errors with ref ids are shown below the corresponding query diff --git a/public/app/features/explore/TraceView/TraceView.tsx b/public/app/features/explore/TraceView/TraceView.tsx index 109659507d0..338ecab95a7 100644 --- a/public/app/features/explore/TraceView/TraceView.tsx +++ b/public/app/features/explore/TraceView/TraceView.tsx @@ -1,7 +1,6 @@ import { css } from '@emotion/css'; import { TopOfViewRefType } from '@jaegertracing/jaeger-ui-components/src/TraceTimelineViewer/VirtualizedTraceView'; import React, { RefObject, useCallback, useMemo, useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; import { DataFrame, @@ -30,7 +29,7 @@ import { TraceToMetricsData } from 'app/core/components/TraceToMetrics/TraceToMe import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { getTimeZone } from 'app/features/profile/state/selectors'; import { TempoQuery } from 'app/plugins/datasource/tempo/types'; -import { StoreState } from 'app/types'; +import { useDispatch, useSelector } from 'app/types'; import { ExploreId } from 'app/types/explore'; import { changePanelState } from '../state/explorePane'; @@ -138,7 +137,7 @@ export function TraceView(props: Props) { [props.splitOpenFn, traceToLogsOptions, traceToMetricsOptions, props.dataFrames, createFocusSpanLink] ); const onSlimViewClicked = useCallback(() => setSlim(!slim), [slim]); - const timeZone = useSelector((state: StoreState) => getTimeZone(state.user)); + const timeZone = useSelector((state) => getTimeZone(state.user)); const datasourceType = datasource ? datasource?.type : 'unknown'; return ( @@ -218,7 +217,7 @@ function useFocusSpanLink(options: { refId?: string; datasource?: DataSourceApi; }): [string | undefined, (traceId: string, spanId: string) => LinkModel] { - const panelState = useSelector((state: StoreState) => state.explore[options.exploreId]?.panelsState.trace); + const panelState = useSelector((state) => state.explore[options.exploreId]?.panelsState.trace); const focusedSpanId = panelState?.spanId; const dispatch = useDispatch(); @@ -230,7 +229,7 @@ function useFocusSpanLink(options: { }) ); - const query = useSelector((state: StoreState) => + const query = useSelector((state) => state.explore[options.exploreId]?.queries.find((query) => query.refId === options.refId) ); diff --git a/public/app/features/explore/TraceView/TraceViewContainer.tsx b/public/app/features/explore/TraceView/TraceViewContainer.tsx index db2a3654356..6bdadacea77 100644 --- a/public/app/features/explore/TraceView/TraceViewContainer.tsx +++ b/public/app/features/explore/TraceView/TraceViewContainer.tsx @@ -1,11 +1,10 @@ import TracePageSearchBar from '@jaegertracing/jaeger-ui-components/src/TracePageHeader/TracePageSearchBar'; import { TopOfViewRefType } from '@jaegertracing/jaeger-ui-components/src/TraceTimelineViewer/VirtualizedTraceView'; import React, { RefObject, useMemo, useState } from 'react'; -import { useSelector } from 'react-redux'; import { DataFrame, SplitOpen, PanelData } from '@grafana/data'; import { Collapse } from '@grafana/ui'; -import { StoreState } from 'app/types'; +import { StoreState, useSelector } from 'app/types'; import { ExploreId } from 'app/types/explore'; import { TraceView } from './TraceView'; diff --git a/public/app/features/explore/state/main.ts b/public/app/features/explore/state/main.ts index f4fd3263110..1a61b49e714 100644 --- a/public/app/features/explore/state/main.ts +++ b/public/app/features/explore/state/main.ts @@ -1,7 +1,7 @@ import { createAction } from '@reduxjs/toolkit'; import { AnyAction } from 'redux'; -import { ExploreUrlState, serializeStateToUrlParam, SplitOpen, UrlQueryMap } from '@grafana/data'; +import { DataQuery, ExploreUrlState, serializeStateToUrlParam, SplitOpenOptions, UrlQueryMap } from '@grafana/data'; import { DataSourceSrv, locationService } from '@grafana/runtime'; import { GetExploreUrlArguments, stopQueryState } from 'app/core/utils/explore'; import { PanelModel } from 'app/features/dashboard/state'; @@ -101,7 +101,7 @@ export const lastSavedUrl: UrlQueryMap = {}; * or uses values from options arg. This does only navigation each pane is then responsible for initialization from * the URL. */ -export const splitOpen: SplitOpen = (options): ThunkResult => { +export const splitOpen = (options?: SplitOpenOptions): ThunkResult => { return async (dispatch, getState) => { const leftState: ExploreItemState = getState().explore[ExploreId.left]; const leftUrlState = getUrlStateFromPaneState(leftState); diff --git a/public/app/features/explore/useLiveTailControls.ts b/public/app/features/explore/useLiveTailControls.ts index 84004381429..6b9ed004d93 100644 --- a/public/app/features/explore/useLiveTailControls.ts +++ b/public/app/features/explore/useLiveTailControls.ts @@ -1,7 +1,7 @@ import React, { useCallback } from 'react'; -import { useDispatch } from 'react-redux'; import { RefreshPicker } from '@grafana/ui'; +import { useDispatch } from 'app/types'; import { ExploreId } from '../../types'; diff --git a/public/app/features/folders/FolderAlerting.tsx b/public/app/features/folders/FolderAlerting.tsx index ec4c7615356..3e44468c1ba 100644 --- a/public/app/features/folders/FolderAlerting.tsx +++ b/public/app/features/folders/FolderAlerting.tsx @@ -1,11 +1,10 @@ import React from 'react'; -import { useDispatch, useSelector } from 'react-redux'; import { useAsync } from 'react-use'; import { Page } from 'app/core/components/Page/Page'; import { GrafanaRouteComponentProps } from 'app/core/navigation/types'; import { getNavModel } from 'app/core/selectors/navModel'; -import { StoreState } from 'app/types'; +import { useDispatch, useSelector } from 'app/types'; import { AlertsFolderView } from '../alerting/unified/AlertsFolderView'; @@ -16,8 +15,8 @@ export interface OwnProps extends GrafanaRouteComponentProps<{ uid: string }> {} const FolderAlerting = ({ match }: OwnProps) => { const dispatch = useDispatch(); - const navIndex = useSelector((state: StoreState) => state.navIndex); - const folder = useSelector((state: StoreState) => state.folder); + const navIndex = useSelector((state) => state.navIndex); + const folder = useSelector((state) => state.folder); const uid = match.params.uid; const pageNav = getNavModel(navIndex, `folder-alerting-${uid}`, getLoadingNav(1)); diff --git a/public/app/features/library-panels/components/PanelLibraryOptionsGroup/PanelLibraryOptionsGroup.tsx b/public/app/features/library-panels/components/PanelLibraryOptionsGroup/PanelLibraryOptionsGroup.tsx index f61cdb81276..facd4dc0a28 100644 --- a/public/app/features/library-panels/components/PanelLibraryOptionsGroup/PanelLibraryOptionsGroup.tsx +++ b/public/app/features/library-panels/components/PanelLibraryOptionsGroup/PanelLibraryOptionsGroup.tsx @@ -1,12 +1,12 @@ import { css } from '@emotion/css'; import React, { FC, useCallback, useState } from 'react'; -import { useDispatch } from 'react-redux'; import { GrafanaTheme2, PanelPluginMeta } from '@grafana/data'; import { Button, useStyles2, VerticalGroup } from '@grafana/ui'; import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv'; import { PanelModel } from 'app/features/dashboard/state'; import { changeToLibraryPanel } from 'app/features/panel/state/actions'; +import { useDispatch } from 'app/types'; import { PanelTypeFilter } from '../../../../core/components/PanelTypeFilter/PanelTypeFilter'; import { LibraryElementDTO } from '../../types'; diff --git a/public/app/features/library-panels/utils/usePanelSave.ts b/public/app/features/library-panels/utils/usePanelSave.ts index 4bad594efa6..f7034f32736 100644 --- a/public/app/features/library-panels/utils/usePanelSave.ts +++ b/public/app/features/library-panels/utils/usePanelSave.ts @@ -1,11 +1,11 @@ import { t } from '@lingui/macro'; import { useEffect } from 'react'; -import { useDispatch } from 'react-redux'; import useAsyncFn from 'react-use/lib/useAsyncFn'; import { isFetchError } from '@grafana/runtime'; import { notifyApp } from 'app/core/actions'; import { PanelModel } from 'app/features/dashboard/state'; +import { useDispatch } from 'app/types'; import { createPanelLibraryErrorNotification, diff --git a/public/app/features/panel/components/PanelDataErrorView.tsx b/public/app/features/panel/components/PanelDataErrorView.tsx index a5173f79a9c..73d8b7ae064 100644 --- a/public/app/features/panel/components/PanelDataErrorView.tsx +++ b/public/app/features/panel/components/PanelDataErrorView.tsx @@ -1,6 +1,5 @@ import { css } from '@emotion/css'; import React from 'react'; -import { useDispatch } from 'react-redux'; import { CoreApp, GrafanaTheme2, PanelDataSummary, VisualizationSuggestionsBuilder } from '@grafana/data'; import { PanelDataErrorViewProps } from '@grafana/runtime'; @@ -11,6 +10,7 @@ import store from 'app/core/store'; import { toggleVizPicker } from 'app/features/dashboard/components/PanelEditor/state/reducers'; import { VisualizationSelectPaneTab } from 'app/features/dashboard/components/PanelEditor/types'; import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv'; +import { useDispatch } from 'app/types'; import { changePanelPlugin } from '../state/actions'; diff --git a/public/app/features/plugins/admin/components/GetStartedWithPlugin/GetStartedWithDataSource.tsx b/public/app/features/plugins/admin/components/GetStartedWithPlugin/GetStartedWithDataSource.tsx index 7f4e9cdf903..d38c50db68e 100644 --- a/public/app/features/plugins/admin/components/GetStartedWithPlugin/GetStartedWithDataSource.tsx +++ b/public/app/features/plugins/admin/components/GetStartedWithPlugin/GetStartedWithDataSource.tsx @@ -1,9 +1,9 @@ import React, { useCallback } from 'react'; -import { useDispatch } from 'react-redux'; import { DataSourcePluginMeta } from '@grafana/data'; import { Button } from '@grafana/ui'; import { addDataSource } from 'app/features/datasources/state/actions'; +import { useDispatch } from 'app/types'; import { isDataSourceEditor } from '../../permissions'; import { CatalogPlugin } from '../../types'; diff --git a/public/app/features/plugins/admin/pages/Browse.tsx b/public/app/features/plugins/admin/pages/Browse.tsx index af73faf228c..59eb23f4e76 100644 --- a/public/app/features/plugins/admin/pages/Browse.tsx +++ b/public/app/features/plugins/admin/pages/Browse.tsx @@ -1,6 +1,5 @@ import { css } from '@emotion/css'; import React, { ReactElement } from 'react'; -import { useSelector } from 'react-redux'; import { useLocation } from 'react-router-dom'; import { SelectableValue, GrafanaTheme2 } from '@grafana/data'; @@ -9,7 +8,7 @@ import { LoadingPlaceholder, Select, RadioButtonGroup, useStyles2, Tooltip } fro import { Page } from 'app/core/components/Page/Page'; import { GrafanaRouteComponentProps } from 'app/core/navigation/types'; import { getNavModel } from 'app/core/selectors/navModel'; -import { StoreState } from 'app/types/store'; +import { useSelector } from 'app/types'; import { HorizontalGroup } from '../components/HorizontalGroup'; import { PluginList } from '../components/PluginList'; @@ -22,7 +21,7 @@ import { PluginListDisplayMode } from '../types'; export default function Browse({ route }: GrafanaRouteComponentProps): ReactElement | null { const location = useLocation(); const locationSearch = locationSearchToObject(location.search); - const navModel = useSelector((state: StoreState) => getNavModel(state.navIndex, 'plugins')); + const navModel = useSelector((state) => getNavModel(state.navIndex, 'plugins')); const { displayMode, setDisplayMode } = useDisplayMode(); const styles = useStyles2(getStyles); const history = useHistory(); diff --git a/public/app/features/plugins/admin/state/hooks.ts b/public/app/features/plugins/admin/state/hooks.ts index a8790eea9b4..a4032c82fa0 100644 --- a/public/app/features/plugins/admin/state/hooks.ts +++ b/public/app/features/plugins/admin/state/hooks.ts @@ -1,10 +1,10 @@ import { useEffect } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; import { PluginError } from '@grafana/data'; +import { useDispatch, useSelector } from 'app/types'; import { sortPlugins, Sorters } from '../helpers'; -import { CatalogPlugin, PluginCatalogStoreState, PluginListDisplayMode } from '../types'; +import { CatalogPlugin, PluginListDisplayMode } from '../types'; import { fetchAll, fetchDetails, fetchRemotePlugins, install, uninstall } from './actions'; import { setDisplayMode } from './reducer'; @@ -55,7 +55,7 @@ export const useGetSingle = (id: string): CatalogPlugin | undefined => { useFetchAll(); useFetchDetails(id); - return useSelector((state: PluginCatalogStoreState) => selectById(state, id)); + return useSelector((state) => selectById(state, id)); }; export const useGetErrors = (): PluginError[] => { @@ -120,7 +120,7 @@ export const useFetchAll = () => { export const useFetchDetails = (id: string) => { const dispatch = useDispatch(); - const plugin = useSelector((state: PluginCatalogStoreState) => selectById(state, id)); + const plugin = useSelector((state) => selectById(state, id)); const isNotFetching = !useSelector(selectIsRequestPending(fetchDetails.typePrefix)); const shouldFetch = isNotFetching && plugin && !plugin.details; diff --git a/public/app/features/plugins/plugin_loader.ts b/public/app/features/plugins/plugin_loader.ts index 2c6804e9f3d..6fd8133cf47 100644 --- a/public/app/features/plugins/plugin_loader.ts +++ b/public/app/features/plugins/plugin_loader.ts @@ -6,7 +6,7 @@ import moment from 'moment'; // eslint-disable-line no-restricted-imports import prismjs from 'prismjs'; import react from 'react'; import reactDom from 'react-dom'; -import * as reactRedux from 'react-redux'; +import * as reactRedux from 'react-redux'; // eslint-disable-line no-restricted-imports import * as reactRouter from 'react-router-dom'; import * as redux from 'redux'; import * as rxjs from 'rxjs'; diff --git a/public/app/features/search/hooks/useSearchQuery.ts b/public/app/features/search/hooks/useSearchQuery.ts index 1689db1ac0e..db7f845cdc2 100644 --- a/public/app/features/search/hooks/useSearchQuery.ts +++ b/public/app/features/search/hooks/useSearchQuery.ts @@ -1,10 +1,9 @@ import { debounce } from 'lodash'; import { FormEvent } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; import { SelectableValue } from '@grafana/data'; import { locationService } from '@grafana/runtime'; -import { StoreState } from 'app/types'; +import { useDispatch, useSelector } from 'app/types'; import { defaultQueryParams, @@ -24,7 +23,7 @@ import { hasFilters } from '../utils'; const updateLocation = debounce((query) => locationService.partial(query, true), 300); export const useSearchQuery = (defaults: Partial) => { - const query = useSelector((state: StoreState) => state.searchQuery); + const query = useSelector((state) => state.searchQuery); const dispatch = useDispatch(); const onQueryChange = (query: string) => { diff --git a/public/app/features/variables/textbox/TextBoxVariablePicker.tsx b/public/app/features/variables/textbox/TextBoxVariablePicker.tsx index 1cc3765fde5..dface5d9259 100644 --- a/public/app/features/variables/textbox/TextBoxVariablePicker.tsx +++ b/public/app/features/variables/textbox/TextBoxVariablePicker.tsx @@ -1,7 +1,7 @@ import React, { ChangeEvent, FocusEvent, KeyboardEvent, ReactElement, useCallback, useEffect, useState } from 'react'; -import { useDispatch } from 'react-redux'; import { Input } from '@grafana/ui'; +import { useDispatch } from 'app/types'; import { variableAdapters } from '../adapters'; import { VariablePickerProps } from '../pickers/types'; diff --git a/public/app/plugins/panel/alertGroups/AlertGroupsPanel.tsx b/public/app/plugins/panel/alertGroups/AlertGroupsPanel.tsx index f03a18b217e..9ec750e543c 100644 --- a/public/app/plugins/panel/alertGroups/AlertGroupsPanel.tsx +++ b/public/app/plugins/panel/alertGroups/AlertGroupsPanel.tsx @@ -1,5 +1,4 @@ import React, { useEffect } from 'react'; -import { useDispatch } from 'react-redux'; import { PanelProps } from '@grafana/data'; import { config } from '@grafana/runtime'; @@ -10,6 +9,7 @@ import { parseMatchers } from 'app/features/alerting/unified/utils/alertmanager' import { NOTIFICATIONS_POLL_INTERVAL_MS } from 'app/features/alerting/unified/utils/constants'; import { initialAsyncRequestState } from 'app/features/alerting/unified/utils/redux'; import { AlertmanagerGroup, Matcher } from 'app/plugins/datasource/alertmanager/types'; +import { useDispatch } from 'app/types'; import { AlertGroup } from './AlertGroup'; import { AlertGroupPanelOptions } from './types'; diff --git a/public/app/plugins/panel/alertlist/GroupByWithLoading.tsx b/public/app/plugins/panel/alertlist/GroupByWithLoading.tsx index 0fa1e207bcf..672a54d4c30 100644 --- a/public/app/plugins/panel/alertlist/GroupByWithLoading.tsx +++ b/public/app/plugins/panel/alertlist/GroupByWithLoading.tsx @@ -1,6 +1,5 @@ import { isEmpty, uniq } from 'lodash'; import React, { FC, useEffect, useMemo } from 'react'; -import { useDispatch } from 'react-redux'; import { SelectableValue } from '@grafana/data'; import { Icon, MultiSelect } from '@grafana/ui'; @@ -11,6 +10,7 @@ import { isAsyncRequestMapSliceFulfilled, isAsyncRequestMapSlicePending, } from 'app/features/alerting/unified/utils/redux'; +import { useDispatch } from 'app/types'; import { AlertingRule } from 'app/types/unified-alerting'; import { PromRuleType } from 'app/types/unified-alerting-dto'; diff --git a/public/app/plugins/panel/alertlist/UnifiedAlertList.tsx b/public/app/plugins/panel/alertlist/UnifiedAlertList.tsx index 91cb9a8db4c..ba2c13b7828 100644 --- a/public/app/plugins/panel/alertlist/UnifiedAlertList.tsx +++ b/public/app/plugins/panel/alertlist/UnifiedAlertList.tsx @@ -1,7 +1,6 @@ import { css } from '@emotion/css'; import { sortBy } from 'lodash'; import React, { useEffect, useMemo } from 'react'; -import { useDispatch } from 'react-redux'; import { GrafanaTheme2, PanelProps } from '@grafana/data'; import { @@ -28,7 +27,7 @@ import { } from 'app/features/alerting/unified/utils/datasource'; import { flattenRules, getFirstActiveAt } from 'app/features/alerting/unified/utils/rules'; import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv'; -import { AccessControlAction } from 'app/types'; +import { useDispatch, AccessControlAction } from 'app/types'; import { PromRuleWithLocation } from 'app/types/unified-alerting'; import { PromAlertingRuleState } from 'app/types/unified-alerting-dto'; diff --git a/public/app/plugins/panel/dashlist/DashList.tsx b/public/app/plugins/panel/dashlist/DashList.tsx index 09b741bcd1b..ca3e5824553 100644 --- a/public/app/plugins/panel/dashlist/DashList.tsx +++ b/public/app/plugins/panel/dashlist/DashList.tsx @@ -1,7 +1,6 @@ import { css, cx } from '@emotion/css'; import { take } from 'lodash'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import { useDispatch } from 'react-redux'; import { GrafanaTheme2, InterpolateFunction, PanelProps } from '@grafana/data'; import { CustomScrollbar, stylesFactory, useStyles2 } from '@grafana/ui'; @@ -13,6 +12,7 @@ import impressionSrv from 'app/core/services/impression_srv'; import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv'; import { SearchCard } from 'app/features/search/components/SearchCard'; import { DashboardSearchItem } from 'app/features/search/types'; +import { useDispatch } from 'app/types'; import { PanelLayout, PanelOptions } from './models.gen'; import { getStyles } from './styles'; diff --git a/public/app/store/configureStore.ts b/public/app/store/configureStore.ts index 837185588fd..58f199349fd 100644 --- a/public/app/store/configureStore.ts +++ b/public/app/store/configureStore.ts @@ -33,6 +33,9 @@ export function configureStore(initialState?: Partial) { return store; } +export type RootState = ReturnType['getState']>; +export type AppDispatch = ReturnType['dispatch']; + /* function getActionsToIgnoreSerializableCheckOn() { return [ diff --git a/public/app/types/store.ts b/public/app/types/store.ts index 4786c1f508a..3f2e652d045 100644 --- a/public/app/types/store.ts +++ b/public/app/types/store.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-restricted-imports */ import { Action, AsyncThunk, @@ -14,7 +15,7 @@ import { import { ThunkAction, ThunkDispatch as GenericThunkDispatch } from 'redux-thunk'; import type { createRootReducer } from 'app/core/reducers/root'; -import { configureStore } from 'app/store/configureStore'; +import { AppDispatch, RootState } from 'app/store/configureStore'; export type StoreState = ReturnType>; @@ -26,9 +27,8 @@ export type ThunkResult = ThunkAction; // Typed useDispatch & useSelector hooks -export type AppDispatch = ReturnType['dispatch']; -export const useDispatch = () => useDispatchUntyped(); -export const useSelector: TypedUseSelectorHook = useSelectorUntyped; +export const useDispatch: () => AppDispatch = useDispatchUntyped; +export const useSelector: TypedUseSelectorHook = useSelectorUntyped; type DefaultThunkApiConfig = { dispatch: AppDispatch; state: StoreState }; export const createAsyncThunk = (