This is an automated email from the ASF dual-hosted git repository.
rusackas pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new 3f0858e35de chore(sql-lab): migrate useDispatch to useAppDispatch
(#40037)
3f0858e35de is described below
commit 3f0858e35de5f01b70432b11b3843edfaaa0111f
Author: Evan Rusackas <[email protected]>
AuthorDate: Wed May 20 15:36:27 2026 -0700
chore(sql-lab): migrate useDispatch to useAppDispatch (#40037)
Co-authored-by: Claude Code <[email protected]>
---
superset-frontend/src/SqlLab/actions/sqlLab.ts | 13 +++++++++----
.../src/SqlLab/components/EditorAutoSync/index.tsx | 5 +++--
.../src/SqlLab/components/EditorWrapper/index.tsx | 5 +++--
.../src/SqlLab/components/EditorWrapper/useKeywords.ts | 5 +++--
.../SqlLab/components/ExploreCtasResultsButton/index.tsx | 9 +++++----
.../src/SqlLab/components/PopEditorTab/index.tsx | 5 +++--
.../src/SqlLab/components/QueryAutoRefresh/index.tsx | 5 +++--
.../src/SqlLab/components/QueryLimitSelect/index.tsx | 4 ++--
.../src/SqlLab/components/QueryTable/index.tsx | 5 +++--
superset-frontend/src/SqlLab/components/ResultSet/index.tsx | 5 +++--
superset-frontend/src/SqlLab/components/SouthPane/index.tsx | 5 +++--
superset-frontend/src/SqlLab/components/SqlEditor/index.tsx | 5 +++--
.../src/SqlLab/components/SqlEditorLeftBar/index.tsx | 4 ++--
.../src/SqlLab/components/SqlEditorTabHeader/index.tsx | 5 +++--
.../components/SqlEditorTopBar/useDatabaseSelector.ts | 5 +++--
.../src/SqlLab/components/TableElement/index.tsx | 5 +++--
.../src/SqlLab/components/TableExploreTree/index.tsx | 5 +++--
.../src/SqlLab/components/TableExploreTree/useTreeData.ts | 4 ++--
.../src/SqlLab/components/TablePreview/index.tsx | 5 +++--
superset-frontend/src/views/store.ts | 13 +++++++++++--
20 files changed, 73 insertions(+), 44 deletions(-)
diff --git a/superset-frontend/src/SqlLab/actions/sqlLab.ts
b/superset-frontend/src/SqlLab/actions/sqlLab.ts
index 883f50d40f1..302f0b82cb0 100644
--- a/superset-frontend/src/SqlLab/actions/sqlLab.ts
+++ b/superset-frontend/src/SqlLab/actions/sqlLab.ts
@@ -1712,7 +1712,7 @@ export function createDatasource(
export function createCtasDatasource(
vizOptions: Record<string, unknown>,
-): SqlLabThunkAction<Promise<{ id: number }>> {
+): SqlLabThunkAction<Promise<{ table_id: number }>> {
return (dispatch: AppDispatch) => {
dispatch(createDatasourceStarted());
return SupersetClient.post({
@@ -1720,9 +1720,14 @@ export function createCtasDatasource(
jsonPayload: vizOptions,
})
.then(({ json }) => {
- dispatch(createDatasourceSuccess(json.result));
-
- return json.result;
+ const result = json.result as { table_id: number };
+ // The endpoint's `result.table_id` IS the dataset id; normalize so
+ // createDatasourceSuccess's `${data.id}__table` resolves correctly.
+ // Without this, the CTAS Explore button silently produced
+ // `"undefined__table"` because `result.id` doesn't exist.
+ dispatch(createDatasourceSuccess({ id: result.table_id }));
+
+ return result;
})
.catch(() => {
const errorMsg = t('An error occurred while creating the data source');
diff --git a/superset-frontend/src/SqlLab/components/EditorAutoSync/index.tsx
b/superset-frontend/src/SqlLab/components/EditorAutoSync/index.tsx
index 0a13bcf9c12..a0e1da1d7d0 100644
--- a/superset-frontend/src/SqlLab/components/EditorAutoSync/index.tsx
+++ b/superset-frontend/src/SqlLab/components/EditorAutoSync/index.tsx
@@ -19,7 +19,8 @@
import { useRef, useEffect, FC, useMemo } from 'react';
-import { useDispatch, useSelector } from 'react-redux';
+import { useSelector } from 'react-redux';
+import { useAppDispatch } from 'src/views/store';
import { logging } from '@apache-superset/core/utils';
import {
SqlLabRootState,
@@ -86,7 +87,7 @@ const EditorAutoSync: FC = () => {
const editorTabLastUpdatedAt = useSelector<SqlLabRootState, number>(
state => state.sqlLab.editorTabLastUpdatedAt,
);
- const dispatch = useDispatch();
+ const dispatch = useAppDispatch();
const lastSavedTimestampRef = useRef<number>(editorTabLastUpdatedAt);
const currentQueryEditorId = useSelector<SqlLabRootState, string>(
diff --git a/superset-frontend/src/SqlLab/components/EditorWrapper/index.tsx
b/superset-frontend/src/SqlLab/components/EditorWrapper/index.tsx
index aafaa234402..3a28a534b54 100644
--- a/superset-frontend/src/SqlLab/components/EditorWrapper/index.tsx
+++ b/superset-frontend/src/SqlLab/components/EditorWrapper/index.tsx
@@ -17,7 +17,8 @@
* under the License.
*/
import { useState, useEffect, useRef, useCallback, useMemo } from 'react';
-import { shallowEqual, useDispatch, useSelector } from 'react-redux';
+import { shallowEqual, useSelector } from 'react-redux';
+import { useAppDispatch } from 'src/views/store';
import { usePrevious } from '@superset-ui/core';
import { css, useTheme } from '@apache-superset/core/theme';
import { Global } from '@emotion/react';
@@ -136,7 +137,7 @@ const EditorWrapper = ({
height,
hotkeys,
}: EditorWrapperProps) => {
- const dispatch = useDispatch();
+ const dispatch = useAppDispatch();
const queryEditor = useQueryEditor(queryEditorId, [
'id',
'dbId',
diff --git
a/superset-frontend/src/SqlLab/components/EditorWrapper/useKeywords.ts
b/superset-frontend/src/SqlLab/components/EditorWrapper/useKeywords.ts
index fa1a1974be9..ee0bdcb5a9b 100644
--- a/superset-frontend/src/SqlLab/components/EditorWrapper/useKeywords.ts
+++ b/superset-frontend/src/SqlLab/components/EditorWrapper/useKeywords.ts
@@ -17,7 +17,8 @@
* under the License.
*/
import { useEffect, useMemo, useRef } from 'react';
-import { useDispatch, useStore } from 'react-redux';
+import { useStore } from 'react-redux';
+import { useAppDispatch } from 'src/views/store';
import { t } from '@apache-superset/core/translation';
import { getExtensionsRegistry } from '@superset-ui/core';
@@ -68,7 +69,7 @@ export function useKeywords(
catalog,
schema,
});
- const dispatch = useDispatch();
+ const dispatch = useAppDispatch();
const hasFetchedKeywords = useRef(false);
// skipFetch is used to prevent re-evaluating memoized keywords
// due to updated api results by skip flag
diff --git
a/superset-frontend/src/SqlLab/components/ExploreCtasResultsButton/index.tsx
b/superset-frontend/src/SqlLab/components/ExploreCtasResultsButton/index.tsx
index b7f6978dde8..4cb4bcc0718 100644
--- a/superset-frontend/src/SqlLab/components/ExploreCtasResultsButton/index.tsx
+++ b/superset-frontend/src/SqlLab/components/ExploreCtasResultsButton/index.tsx
@@ -16,9 +16,10 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { useSelector, useDispatch } from 'react-redux';
+import { useSelector } from 'react-redux';
+import { useAppDispatch } from 'src/views/store';
import { t } from '@apache-superset/core/translation';
-import { JsonObject, VizType } from '@superset-ui/core';
+import { VizType } from '@superset-ui/core';
import {
createCtasDatasource,
addInfoToast,
@@ -45,7 +46,7 @@ const ExploreCtasResultsButton = ({
const errorMessage = useSelector(
(state: SqlLabRootState) => state.sqlLab.errorMessage,
);
- const dispatch = useDispatch<(dispatch: any) => Promise<JsonObject>>();
+ const dispatch = useAppDispatch();
const buildVizOptions = {
table_name: table,
@@ -56,7 +57,7 @@ const ExploreCtasResultsButton = ({
const visualize = () => {
dispatch(createCtasDatasource(buildVizOptions))
- .then((data: { table_id: number }) => {
+ .then(data => {
const formData = {
datasource: `${data.table_id}__table`,
metrics: ['count'],
diff --git a/superset-frontend/src/SqlLab/components/PopEditorTab/index.tsx
b/superset-frontend/src/SqlLab/components/PopEditorTab/index.tsx
index 4258cd5899a..24fecab9470 100644
--- a/superset-frontend/src/SqlLab/components/PopEditorTab/index.tsx
+++ b/superset-frontend/src/SqlLab/components/PopEditorTab/index.tsx
@@ -17,7 +17,8 @@
* under the License.
*/
import { useEffect, useState } from 'react';
-import { useDispatch, useSelector } from 'react-redux';
+import { useSelector } from 'react-redux';
+import { useAppDispatch } from 'src/views/store';
import URI from 'urijs';
import { pick } from 'lodash';
import { useComponentDidUpdate } from '@superset-ui/core';
@@ -49,7 +50,7 @@ const PopEditorTab: React.FC<{ children?: React.ReactNode }>
= ({
({ sqlLab: { tabHistory } }) => tabHistory.slice(-1)[0],
);
const [updatedUrl, setUpdatedUrl] = useState<string>(SQL_LAB_URL);
- const dispatch = useDispatch();
+ const dispatch = useAppDispatch();
useComponentDidUpdate(() => {
setQueryEditorId(assigned => assigned ?? activeQueryEditorId);
if (activeQueryEditorId) {
diff --git a/superset-frontend/src/SqlLab/components/QueryAutoRefresh/index.tsx
b/superset-frontend/src/SqlLab/components/QueryAutoRefresh/index.tsx
index 2bf1aca2843..ac1777ea3fe 100644
--- a/superset-frontend/src/SqlLab/components/QueryAutoRefresh/index.tsx
+++ b/superset-frontend/src/SqlLab/components/QueryAutoRefresh/index.tsx
@@ -17,7 +17,8 @@
* under the License.
*/
import { useRef } from 'react';
-import { useSelector, useDispatch } from 'react-redux';
+import { useSelector } from 'react-redux';
+import { useAppDispatch } from 'src/views/store';
import { isObject } from 'lodash';
import rison from 'rison';
import {
@@ -82,7 +83,7 @@ function QueryAutoRefresh({
.map(({ id }) => id),
),
);
- const dispatch = useDispatch();
+ const dispatch = useAppDispatch();
const checkForRefresh = () => {
const shouldRequestChecking = shouldCheckForQueries(queries);
diff --git a/superset-frontend/src/SqlLab/components/QueryLimitSelect/index.tsx
b/superset-frontend/src/SqlLab/components/QueryLimitSelect/index.tsx
index 4c2803b7753..9812ff227e3 100644
--- a/superset-frontend/src/SqlLab/components/QueryLimitSelect/index.tsx
+++ b/superset-frontend/src/SqlLab/components/QueryLimitSelect/index.tsx
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { useDispatch } from 'react-redux';
+import { useAppDispatch } from 'src/views/store';
import { t } from '@apache-superset/core/translation';
import { Dropdown, Button } from '@superset-ui/core/components';
import { Menu } from '@superset-ui/core/components/Menu';
@@ -75,7 +75,7 @@ const QueryLimitSelect = ({
maxRow,
defaultQueryLimit,
}: QueryLimitSelectProps) => {
- const dispatch = useDispatch();
+ const dispatch = useAppDispatch();
const queryEditor = useQueryEditor(queryEditorId, ['id', 'queryLimit']);
const queryLimit = queryEditor.queryLimit || defaultQueryLimit;
diff --git a/superset-frontend/src/SqlLab/components/QueryTable/index.tsx
b/superset-frontend/src/SqlLab/components/QueryTable/index.tsx
index 04f40378947..33c4342a5aa 100644
--- a/superset-frontend/src/SqlLab/components/QueryTable/index.tsx
+++ b/superset-frontend/src/SqlLab/components/QueryTable/index.tsx
@@ -30,7 +30,8 @@ import ProgressBar from
'@superset-ui/core/components/ProgressBar';
import { t } from '@apache-superset/core/translation';
import { QueryResponse, QueryState } from '@superset-ui/core';
import { useTheme } from '@apache-superset/core/theme';
-import { shallowEqual, useDispatch, useSelector } from 'react-redux';
+import { shallowEqual, useSelector } from 'react-redux';
+import { useAppDispatch } from 'src/views/store';
import {
queryEditorSetSql,
@@ -92,7 +93,7 @@ const QueryTable = ({
latestQueryId,
}: QueryTableProps) => {
const theme = useTheme();
- const dispatch = useDispatch();
+ const dispatch = useAppDispatch();
const [selectedQuery, setSelectedQuery] = useState<QueryResponse | null>(
null,
);
diff --git a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx
b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx
index d7c42fc2b2c..5637e3c940a 100644
--- a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx
+++ b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx
@@ -27,7 +27,8 @@ import {
} from 'react';
import AutoSizer from 'react-virtualized-auto-sizer';
-import { shallowEqual, useDispatch, useSelector } from 'react-redux';
+import { shallowEqual, useSelector } from 'react-redux';
+import { useAppDispatch } from 'src/views/store';
import { useHistory } from 'react-router-dom';
import { pick } from 'lodash';
import {
@@ -231,7 +232,7 @@ const ResultSet = ({
canCopyClipboardSqlLab: canCopyClipboard,
} = usePermissions();
const history = useHistory();
- const dispatch = useDispatch();
+ const dispatch = useAppDispatch();
const logAction = useLogAction({ queryId, sqlEditorId: query.sqlEditorId });
const { showConfirm, ConfirmModal } = useConfirmModal();
diff --git a/superset-frontend/src/SqlLab/components/SouthPane/index.tsx
b/superset-frontend/src/SqlLab/components/SouthPane/index.tsx
index 02e16d49c37..8094527eeba 100644
--- a/superset-frontend/src/SqlLab/components/SouthPane/index.tsx
+++ b/superset-frontend/src/SqlLab/components/SouthPane/index.tsx
@@ -17,7 +17,8 @@
* under the License.
*/
import { createRef, useCallback, useMemo } from 'react';
-import { shallowEqual, useDispatch, useSelector } from 'react-redux';
+import { shallowEqual, useSelector } from 'react-redux';
+import { useAppDispatch } from 'src/views/store';
import { nanoid } from 'nanoid';
import Tabs from '@superset-ui/core/components/Tabs';
import { t } from '@apache-superset/core/translation';
@@ -105,7 +106,7 @@ const SouthPane = ({
const { id, tabViewId } = useQueryEditor(queryEditorId, ['tabViewId']);
const editorId = tabViewId ?? id;
const theme = useTheme();
- const dispatch = useDispatch();
+ const dispatch = useAppDispatch();
const viewItems = views.getViews(ViewLocations.sqllab.panels) || [];
const { offline, tables } = useSelector(
({ sqlLab: { offline, tables } }: SqlLabRootState) => ({
diff --git a/superset-frontend/src/SqlLab/components/SqlEditor/index.tsx
b/superset-frontend/src/SqlLab/components/SqlEditor/index.tsx
index c8efac1e9bb..271f87538c7 100644
--- a/superset-frontend/src/SqlLab/components/SqlEditor/index.tsx
+++ b/superset-frontend/src/SqlLab/components/SqlEditor/index.tsx
@@ -30,7 +30,8 @@ import {
import type { editors } from '@apache-superset/core';
import useEffectEvent from 'src/hooks/useEffectEvent';
-import { shallowEqual, useDispatch, useSelector } from 'react-redux';
+import { shallowEqual, useSelector } from 'react-redux';
+import { useAppDispatch } from 'src/views/store';
import AutoSizer from 'react-virtualized-auto-sizer';
import { t } from '@apache-superset/core/translation';
import {
@@ -237,7 +238,7 @@ const SqlEditor: FC<Props> = ({
scheduleQueryWarning,
}) => {
const theme = useTheme();
- const dispatch = useDispatch();
+ const dispatch = useAppDispatch();
const { database, latestQuery, currentQueryEditorId, hasSqlStatement } =
useSelector<
diff --git a/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx
b/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx
index e4297629271..7dfe60f289f 100644
--- a/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx
+++ b/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx
@@ -17,7 +17,7 @@
* under the License.
*/
import { useCallback, useState } from 'react';
-import { useDispatch } from 'react-redux';
+import { useAppDispatch } from 'src/views/store';
import { resetState } from 'src/SqlLab/actions/sqlLab';
import {
@@ -69,7 +69,7 @@ const SqlEditorLeftBar = ({ queryEditorId }:
SqlEditorLeftBarProps) => {
const { db, catalog, schema, onDbChange, onCatalogChange, onSchemaChange } =
dbSelectorProps;
- const dispatch = useDispatch();
+ const dispatch = useAppDispatch();
const shouldShowReset = window.location.search === '?reset=1';
// Modal state for Database/Catalog/Schema selector
diff --git
a/superset-frontend/src/SqlLab/components/SqlEditorTabHeader/index.tsx
b/superset-frontend/src/SqlLab/components/SqlEditorTabHeader/index.tsx
index 6753612a8e9..3f707d25957 100644
--- a/superset-frontend/src/SqlLab/components/SqlEditorTabHeader/index.tsx
+++ b/superset-frontend/src/SqlLab/components/SqlEditorTabHeader/index.tsx
@@ -19,7 +19,8 @@
import { useMemo, FC } from 'react';
import { bindActionCreators } from 'redux';
-import { useSelector, useDispatch, shallowEqual } from 'react-redux';
+import { useSelector, shallowEqual } from 'react-redux';
+import { useAppDispatch } from 'src/views/store';
import { MenuDotsDropdown } from '@superset-ui/core/components';
import { Menu, MenuItemType } from '@superset-ui/core/components/Menu';
import { t } from '@apache-superset/core/translation';
@@ -90,7 +91,7 @@ const SqlEditorTabHeader: FC<Props> = ({ queryEditor }) => {
);
const StatusIcon = queryState ? STATE_ICONS[queryState] :
STATE_ICONS.running;
- const dispatch = useDispatch();
+ const dispatch = useAppDispatch();
const actions = useMemo(
() =>
bindActionCreators(
diff --git
a/superset-frontend/src/SqlLab/components/SqlEditorTopBar/useDatabaseSelector.ts
b/superset-frontend/src/SqlLab/components/SqlEditorTopBar/useDatabaseSelector.ts
index ee561e19c61..83d444418e3 100644
---
a/superset-frontend/src/SqlLab/components/SqlEditorTopBar/useDatabaseSelector.ts
+++
b/superset-frontend/src/SqlLab/components/SqlEditorTopBar/useDatabaseSelector.ts
@@ -17,7 +17,8 @@
* under the License.
*/
import { useEffect, useCallback, useMemo, useState } from 'react';
-import { useDispatch, useSelector } from 'react-redux';
+import { useSelector } from 'react-redux';
+import { useAppDispatch } from 'src/views/store';
import { SqlLabRootState } from 'src/SqlLab/types';
import {
@@ -41,7 +42,7 @@ export default function useDatabaseSelector(queryEditorId:
string) {
SqlLabRootState,
SqlLabRootState['sqlLab']['databases']
>(({ sqlLab }) => sqlLab.databases);
- const dispatch = useDispatch();
+ const dispatch = useAppDispatch();
const queryEditor = useQueryEditor(queryEditorId, [
'dbId',
'catalog',
diff --git a/superset-frontend/src/SqlLab/components/TableElement/index.tsx
b/superset-frontend/src/SqlLab/components/TableElement/index.tsx
index 710f2eb4bdc..8ca87460387 100644
--- a/superset-frontend/src/SqlLab/components/TableElement/index.tsx
+++ b/superset-frontend/src/SqlLab/components/TableElement/index.tsx
@@ -17,7 +17,8 @@
* under the License.
*/
import { useState, useRef, useEffect } from 'react';
-import { useDispatch, useSelector } from 'react-redux';
+import { useSelector } from 'react-redux';
+import { useAppDispatch } from 'src/views/store';
import type { QueryEditor, SqlLabRootState, Table } from 'src/SqlLab/types';
import {
ButtonGroup,
@@ -75,7 +76,7 @@ const Fade = styled.div`
const TableElement = ({ table, ...props }: TableElementProps) => {
const { dbId, catalog, schema, name, expanded, id } = table;
const theme = useTheme();
- const dispatch = useDispatch();
+ const dispatch = useAppDispatch();
const {
currentData: tableMetadata,
isSuccess: isMetadataSuccess,
diff --git a/superset-frontend/src/SqlLab/components/TableExploreTree/index.tsx
b/superset-frontend/src/SqlLab/components/TableExploreTree/index.tsx
index 60ce2a2b47c..d6804771b7a 100644
--- a/superset-frontend/src/SqlLab/components/TableExploreTree/index.tsx
+++ b/superset-frontend/src/SqlLab/components/TableExploreTree/index.tsx
@@ -25,7 +25,8 @@ import {
type ChangeEvent,
useMemo,
} from 'react';
-import { useSelector, useDispatch, shallowEqual } from 'react-redux';
+import { useSelector, shallowEqual } from 'react-redux';
+import { useAppDispatch } from 'src/views/store';
import { styled, css, useTheme } from '@apache-superset/core/theme';
import { t } from '@apache-superset/core/translation';
import AutoSizer from 'react-virtualized-auto-sizer';
@@ -163,7 +164,7 @@ const savePinnedSchemasToStorage = (
};
const TableExploreTree: React.FC<Props> = ({ queryEditorId }) => {
- const dispatch = useDispatch();
+ const dispatch = useAppDispatch();
const theme = useTheme();
const treeRef = useRef<TreeApi<TreeNodeData>>(null);
const tables = useSelector(
diff --git
a/superset-frontend/src/SqlLab/components/TableExploreTree/useTreeData.ts
b/superset-frontend/src/SqlLab/components/TableExploreTree/useTreeData.ts
index a61ea25c6ca..239f1f19c1c 100644
--- a/superset-frontend/src/SqlLab/components/TableExploreTree/useTreeData.ts
+++ b/superset-frontend/src/SqlLab/components/TableExploreTree/useTreeData.ts
@@ -17,7 +17,7 @@
* under the License.
*/
import { useMemo, useReducer, useCallback } from 'react';
-import { useDispatch } from 'react-redux';
+import { useAppDispatch } from 'src/views/store';
import { t } from '@apache-superset/core/translation';
import {
Table,
@@ -130,7 +130,7 @@ const useTreeData = ({
catalog,
pinnedTables,
}: UseTreeDataParams): UseTreeDataResult => {
- const reduxDispatch = useDispatch();
+ const reduxDispatch = useAppDispatch();
// Schema data from API
const {
currentData: schemaData,
diff --git a/superset-frontend/src/SqlLab/components/TablePreview/index.tsx
b/superset-frontend/src/SqlLab/components/TablePreview/index.tsx
index 9fbcc3c1264..9e9b72c0c4c 100644
--- a/superset-frontend/src/SqlLab/components/TablePreview/index.tsx
+++ b/superset-frontend/src/SqlLab/components/TablePreview/index.tsx
@@ -17,7 +17,8 @@
* under the License.
*/
import { type FC, useCallback, useMemo, useRef, useState } from 'react';
-import { shallowEqual, useDispatch, useSelector } from 'react-redux';
+import { shallowEqual, useSelector } from 'react-redux';
+import { useAppDispatch } from 'src/views/store';
import { nanoid } from 'nanoid';
import { t } from '@apache-superset/core/translation';
import { ClientErrorObject, getExtensionsRegistry } from '@superset-ui/core';
@@ -110,7 +111,7 @@ const renderWell = (partitions:
TableMetaData['partitions']) => {
};
const TablePreview: FC<Props> = ({ dbId, catalog, schema, tableName }) => {
- const dispatch = useDispatch();
+ const dispatch = useAppDispatch();
const theme = useTheme();
const [databaseName, backend, disableDataPreview] = useSelector<
SqlLabRootState,
diff --git a/superset-frontend/src/views/store.ts
b/superset-frontend/src/views/store.ts
index e6e996f6aba..bf03cf35c02 100644
--- a/superset-frontend/src/views/store.ts
+++ b/superset-frontend/src/views/store.ts
@@ -22,12 +22,13 @@ import {
createListenerMiddleware,
StoreEnhancer,
} from '@reduxjs/toolkit';
+import type { AnyAction } from 'redux';
import {
useDispatch,
useSelector,
type TypedUseSelectorHook,
} from 'react-redux';
-import thunk from 'redux-thunk';
+import thunk, { type ThunkDispatch } from 'redux-thunk';
import { api } from 'src/hooks/apiResources/queryApi';
import messageToastReducer from 'src/components/MessageToasts/reducers';
import charts from 'src/components/Chart/chartReducer';
@@ -188,6 +189,14 @@ export type RootState = ReturnType<typeof store.getState>;
// thunks resolve correctly), and `useAppSelector` infers `RootState` without
// callers having to annotate every selector. Required ahead of the
// react-redux v8+ bump, which tightens dispatch typing — see #39927.
-export type AppDispatch = typeof store.dispatch;
+//
+// AppDispatch is declared as ThunkDispatch & store.dispatch rather than
+// `typeof store.dispatch` because Superset annotates getMiddleware as
+// ConfigureStoreOptions['middleware'], which erases the middleware tuple type
+// and leaves store.dispatch typed as Dispatch<AnyAction>. The intersection
+// restores thunk support without requiring a wider refactor of the middleware
+// setup.
+export type AppDispatch = ThunkDispatch<RootState, undefined, AnyAction> &
+ typeof store.dispatch;
export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;