fix(playground): stabilize onQueryChange callback to prevent stale closures#10815
fix(playground): stabilize onQueryChange callback to prevent stale closures#10815
Conversation
…osures The onQueryChange callback in useQueryBuilder was captured in a useEffect closure but not included in the dependency array. When the parent component provided a new callback (e.g. after creating a new exploration or switching reports in the IDE), the stale callback would be called instead. This caused report creation in the IDE to appear to succeed (the chart renders because the data query works) while the save callback — provided by the parent — was stale and pointed to the wrong context. Fix: wrap onQueryChange with useEvent (already available in the codebase) to ensure the latest callback is always invoked without adding it to the useEffect dependency array (which would cause unnecessary re-fires). Co-authored-by: Artyom Keydunov <artyom.keydunov@gmail.com>
|
Claude finished @keydunov's task in 1m 5s —— View job PR Review: fix(playground): stabilize onQueryChange callback to prevent stale closures
Verdict: Approve — This is a clean, correct fix for a real stale-closure bug. AnalysisThe bug: The fix wraps Code QualityThe implementation is concise and idiomatic: const onQueryChangeStable = useEvent(
(...args: Parameters<NonNullable<typeof onQueryChange>>) => {
onQueryChange?.(...args);
}
);
Minor ObservationThe Security / PerformanceNo concerns. This is a pure React callback stabilization with no impact on data flow, API calls, or user input handling. TestingThe PR doesn't add tests, which is reasonable — this is a closure-timing fix in a React hook that's difficult to unit-test without a full integration harness. The fix uses a well-tested pattern ( SummaryMinimal, surgical fix that follows established codebase conventions. The |
Check List
Issue Reference this PR resolves
CUB-2396 — Handle report creation that does not save in IDE
Description of Changes Made
The
onQueryChangecallback in theuseQueryBuilderhook was captured in auseEffectclosure but not included in the dependency array. This is a classic React stale-closure bug.When the parent component (e.g., the IDE's Explore or report creation view) provides a new
onQueryChangecallback — for example, after creating a new exploration or switching between reports — the old callback was still invoked instead of the new one. This caused report creation in the IDE to appear to succeed (the chart renders because the Core Data API query works fine) while the save/persist callback, provided by the parent, was stale and pointed to the wrong context.Fix: Wrap
onQueryChangewith the existinguseEventhook (auseCallback+ref pattern already available in the codebase) to ensure the latest callback is always invoked without adding it to theuseEffectdependency array (which would cause unnecessary re-fires on every parent render).Changed file:
packages/cubejs-playground/src/QueryBuilderV2/hooks/query-builder.tsThe
useEventhook is a shim of the React RFC foruseEvent— it stores the latest callback in a ref and returns a stable function identity, solving both the stale closure and unnecessary effect re-triggering problems.Linear Issue: CUB-2396