RTK Query subscriptions do not update in React portals rendered into a new window
What I’m doing
I’m building a feature where the user can “pop out” a panel into a new browser window. I create the new window and render React content into it via createPortal:
const [container, setContainer] = useState(null);
const [panelWindow, setPanelWindow] = useState(null);
useEffect(() => {
const popup = window.open("", "_blank", "width=600,height=800");
if (!popup) return;
const el = popup.document.createElement("div");
popup.document.body.appendChild(el);
setPanelWindow(popup);
setContainer(el);
return () => popup.close();
}, []);
if (!container || !panelWindow) return null;
//wrapped by the same Redux <Provider>
return createPortal(
<Panel />,
container
);
The Panel contains RTK Query hooks (useGetXQuery, useMutation, etc.) that subscribe to data.
What works • If I use vanilla Redux slices (dispatch / useSelector), the panel updates correctly in the new window. • If I use axios + useState, it also works fine. • RTK Query mutations (usePatchSomethingMutation) dispatch correctly and update the cache. • Redux DevTools shows the cache entry as updated and fulfilled.
What breaks • Components rendered into the new window with RTK Query hooks (useGetXQuery) do not re-render when the cache updates. • debugValue inside the hook shows the updated data (e.g. 39 rows), but data / currentData in the component remain stale (e.g. 38 rows). • If I force any re-render (resizing, dispatching a dummy action, or making the main window visible by even a single pixel), the panel updates immediately with the correct data. • This only happens when the new window is maximized; if the main window is visible even slightly or the popup is resized, the panel updates immediately.
Why I think this is happening • RTK Query hooks rely on useSyncExternalStore subscriptions. • useSyncExternalStore seems tied to the main document visibility/focus for scheduling updates. • Since the component tree is portaled into a different window.document, subscriptions don’t flush updates until the main document becomes visible again. • Plain Redux works because reducers just update state immediately, without visibility checks.
Steps to reproduce
- Render an RTK Query-enabled component into a new window using createPortal.
- Maximize the new window.
- Trigger a mutation that updates the cached query data.
- Observe: DevTools shows the cache updated, but the new window UI does not.
- Force a re-render (resize window, dispatch dummy action, show main document). Now the new window updates.
Expected behavior RTK Query subscriptions should notify and re-render components in any window as long as they are part of the same React tree + Redux store. Visibility of the original document should not matter.
Workarounds tried • refetch() → does not help. • Dispatching a dummy action → works, but hacky. • Separate React root in popup with <Provider store={store}> → still broken for RTK Query (but plain Redux works fine).
Environment • React 19.1.0 • Redux Toolkit 2.9.0 • Browser: Chrome (latest), also reproduced in Firefox
Here is a minimal reproducible example: https://codesandbox.io/p/sandbox/zpqhcz
It might not be useSyncExternalStore per se, but rather the autoBatch enhancer, which defaults to using requestAnimationFrame as the timing mechanism to trigger some updates:
- https://redux-toolkit.js.org/api/autoBatchEnhancer
What happens if you override your configureStore setup to customize this with one of the other timing options?