TODO: Adopt React 18 concurrent features now that the upgrade has landed #39890
Copy link
Copy link
Open
Labels
frontend:refactorRelated to refactoring the frontendRelated to refactoring the frontend
Description
Now that #38563 has landed React 18, none of the concurrent-rendering APIs are actually being used yet. This is a tracking issue for adopting the features that move the needle on Superset's perceived performance and forward-compat.
Priorities are ordered roughly by ROI / dependency order — earlier items unblock or de-risk later ones.
Foundation
- Enable
<StrictMode>at the root (superset-frontend/src/views/index.tsx) — landed in feat(frontend): enable React StrictMode at root #39893. - Upgrade
react-reduxv7.2.9 → v8+ — split into sub-issue Migrate to react-redux v8 with a typed AppDispatch / useAppDispatch #39927 because v8's stricteruseDispatch/connecttyping requires a typeduseAppDispatchmigration across many files. Doesn't blockuseDeferredValuework; only blocksuseTransitionadoption (tearing risk under concurrent rendering).
Concurrent-rendering wins
- Replace
useDebounceValuewithuseDeferredValuein inputs that filter large client-side lists.useDeferredValueis strictly better than a fixed-delay debounce: deferred updates get interrupted on each keystroke and run at low priority. Targets:SqlLab/components/TableExploreTree/index.tsx:317(PR perf(sql-lab): debounce schema browser search #39489 introduced the debounce — this is the natural follow-up)explore/components/DatasourcePanel/index.tsx:163(column search)packages/superset-ui-core/src/components/Select/Select.tsx:387(used app-wide)explore/components/controls/TextControl/index.tsx:90filters/components/Select/SelectFilterPlugin.tsx:265dashboard/components/nativeFilters/FilterBar/index.tsx:97(publishDataMask)
- Wrap heavy cascading Redux dispatches in
startTransitionso the input that triggered them stays responsive. Highest-impact site: native filter application (changing a filter today blocks the input on chart re-renders). Pair withuseTransition'sisPendingto drive a subtle "applying…" indicator on affected chart cards. Blocked by Migrate to react-redux v8 with a typed AppDispatch / useAppDispatch #39927 — needs typed dispatch + tearing-free store reads.dashboard/components/nativeFilters/FilterBar/index.tsx:268(updateDataMask)dashboard/components/nativeFilters/FilterBar/index.tsx:455(saveChartCustomization)explore/components/ExploreViewContainer/index.tsx:468(triggerQueryafter control change)SqlLab/components/TabbedSqlEditors/index.tsx:145(tab switch)
Lower-priority / opportunistic
-
useIdaudit — mostnanoidusage in the codebase is for persistent data keys (correct, leave alone). The few real DOM-id sites (e.g.DateFilterLabel.tsx:374-375) could move touseIdfor hygiene but aren't urgent. - Suspense coverage — already ~95% on lazy boundaries. Skip unless a specific gap appears.
Notes / risks
- StrictMode rollout: turning it on may surface failures in component tests that assume effects run exactly once. Plan for a cleanup pass; don't merge alongside other work.
- Selector hygiene under concurrent rendering: selectors returning a new object reference per call (
useSelector(state => ({a, b}))) become a tearing risk. Worth a sweep alongside the react-redux v8 bump (tracked in Migrate to react-redux v8 with a typed AppDispatch / useAppDispatch #39927). - Measurement: profile each concurrent-feature PR with React DevTools Profiler before/after. The wins are perceptual; numbers help defend the change.
Suggested PR sequencing
StrictMode + the resulting effect fixes— done in feat(frontend): enable React StrictMode at root #39893useDeferredValuemigration (SQL Lab tree first, core Select after) — does not require v8- react-redux v8 typed-dispatch migration — see Migrate to react-redux v8 with a typed AppDispatch / useAppDispatch #39927
startTransitionaround native filter apply, withisPendingindicator — depends on (3)
PRs should reference this issue.
Reactions are currently unavailable
Metadata
Metadata
Labels
frontend:refactorRelated to refactoring the frontendRelated to refactoring the frontend
Activity
[-]Adopt React 18 concurrent features now that the upgrade has landed[/-][+]TODO: Adopt React 18 concurrent features now that the upgrade has landed[/+]rusackas commentedon May 7, 2026
Quick update from a first attempt at the react-redux v8 bump:
Tried
react-redux@8.1.3. v8 tighteneduseDispatch/connecttyping such that thunks no longer auto-resolve againstDispatch<AnyAction>. A spot-check on three SqlLab files surfaced ~50 new TS errors (SqlLabThunkAction<X> is not assignable to AnyAction, etc.). Across the whole codebase that's a multi-hundred-file migration to a typeduseAppDispatchhook — too big to bundle into this issue.Spun it off into #39927 (Migrate to react-redux v8 with a typed AppDispatch / useAppDispatch). It's now a sub-issue rather than a checklist item here, so it doesn't block the rest of the concurrent-feature work.
Importantly:
useDeferredValuemigrations don't depend on the v8 bump — the tearing risk that v8 mitigates only manifests underuseTransition. So we can keep shippinguseDeferredValuePRs against v7 in the meantime, anduseTransitionadoption later will block on (or co-land with) the v8 effort.Moving on to the
useDeferredValuemigration starting withSqlLab/components/TableExploreTree/index.tsx(the recently-debounced search).venkatamandavilli-code commentedon May 17, 2026
Thanks for reporting this. The behavior seems confusing, especially if the update is saved successfully but the older value still appears afterward.
At first glance, this feels more like a refresh or metadata persistence issue rather than the actual save failing. It may help to verify whether the API response already contains the updated value before the UI reload happens.
A hard refresh or testing in another session could also help narrow down whether caching is involved.
Thanks again for documenting the steps clearly.
perf(filters): use useDeferredValue in SelectFilterPlugin
perf(explore): use useDeferredValue for TextControl inputs
perf(explore): use useDeferredValue for TextControl inputs
perf(explore): use useDeferredValue for TextControl inputs