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;

Reply via email to