Fix react query cache not being cleared when switching budgets (#6953)

* Fix react query cache not being cleared when switching budgets

* React does not want to export function from src/index

* Release note
This commit is contained in:
Joel Jeremy Marquez
2026-02-12 12:12:51 -08:00
committed by GitHub
parent 155e4df219
commit 003efecc23
7 changed files with 71 additions and 48 deletions

View File

@@ -100,10 +100,11 @@ export const loadBudget = createAppAsyncThunk(
export const closeBudget = createAppAsyncThunk(
`${sliceName}/closeBudget`,
async (_, { dispatch, getState }) => {
async (_, { dispatch, getState, extra: { queryClient } }) => {
const prefs = getState().prefs.local;
if (prefs && prefs.id) {
await dispatch(resetApp());
queryClient.clear();
await dispatch(setAppState({ loadingText: t('Closing...') }));
await send('close-budget');
await dispatch(setAppState({ loadingText: null }));
@@ -116,10 +117,11 @@ export const closeBudget = createAppAsyncThunk(
export const closeBudgetUI = createAppAsyncThunk(
`${sliceName}/closeBudgetUI`,
async (_, { dispatch, getState }) => {
async (_, { dispatch, getState, extra: { queryClient } }) => {
const prefs = getState().prefs.local;
if (prefs && prefs.id) {
await dispatch(resetApp());
queryClient.clear();
}
},
);

View File

@@ -1,21 +1,18 @@
import React from 'react';
import { Provider } from 'react-redux';
import { theme } from '@actual-app/components/theme';
import { render, screen } from '@testing-library/react';
import { Change } from './Change';
import { configureAppStore } from '@desktop-client/redux/store';
const store = configureAppStore();
import { TestProviders } from '@desktop-client/mocks';
describe('Change', () => {
it('renders a positive amount with a plus sign and positive color', () => {
render(
<Provider store={store}>
<TestProviders>
<Change amount={12345} />
</Provider>,
</TestProviders>,
);
const el = screen.getByText('+123.45');
expect(el).toBeInTheDocument();
@@ -24,9 +21,9 @@ describe('Change', () => {
it('renders zero with a plus sign and neutral color', () => {
render(
<Provider store={store}>
<TestProviders>
<Change amount={0} />
</Provider>,
</TestProviders>,
);
const el = screen.getByText('+0.00');
expect(el).toBeInTheDocument();
@@ -35,9 +32,9 @@ describe('Change', () => {
it('renders a negative amount with a minus sign and negative color', () => {
render(
<Provider store={store}>
<TestProviders>
<Change amount={-9876} />
</Provider>,
</TestProviders>,
);
const el = screen.getByText('-98.76');
expect(el).toBeInTheDocument();
@@ -46,9 +43,9 @@ describe('Change', () => {
it('merges custom style prop', () => {
render(
<Provider store={store}>
<TestProviders>
<Change amount={1000} style={{ fontWeight: 'bold' }} />
</Provider>,
</TestProviders>,
);
const el = screen.getByText('+10.00');
expect(el).toHaveStyle('font-weight: bold');

View File

@@ -35,7 +35,7 @@ import * as usersSlice from './users/usersSlice';
const queryClient = new QueryClient();
window.__TANSTACK_QUERY_CLIENT__ = queryClient;
const store = configureAppStore();
const store = configureAppStore({ queryClient });
const boundActions = bindActionCreators(
{
@@ -90,15 +90,15 @@ window.$q = q;
const container = document.getElementById('root');
const root = createRoot(container);
root.render(
<Provider store={store}>
<ServerProvider>
<AuthProvider>
<QueryClientProvider client={queryClient}>
<QueryClientProvider client={queryClient}>
<Provider store={store}>
<ServerProvider>
<AuthProvider>
<App />
</QueryClientProvider>
</AuthProvider>
</ServerProvider>
</Provider>,
</AuthProvider>
</ServerProvider>
</Provider>
</QueryClientProvider>,
);
declare global {

View File

@@ -4,22 +4,37 @@ import { Provider } from 'react-redux';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { configureAppStore } from './redux/store';
import type { AppStore } from './redux/store';
let mockQueryClient = new QueryClient();
let mockStore: AppStore = configureAppStore();
export function createTestQueryClient() {
return new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
});
}
export function configureTestAppStore(
...args: Parameters<typeof configureAppStore>
) {
return configureAppStore(...args);
}
let testQueryClient = createTestQueryClient();
let testStore = configureTestAppStore({
queryClient: testQueryClient,
});
export function resetTestProviders() {
mockQueryClient = new QueryClient();
mockStore = configureAppStore();
testQueryClient = createTestQueryClient();
testStore = configureTestAppStore({ queryClient: testQueryClient });
}
export function TestProviders({ children }: { children: ReactNode }) {
return (
<Provider store={mockStore}>
<QueryClientProvider client={mockQueryClient}>
{children}
</QueryClientProvider>
</Provider>
<QueryClientProvider client={testQueryClient}>
<Provider store={testStore}>{children}</Provider>
</QueryClientProvider>
);
}

View File

@@ -7,11 +7,12 @@ import {
import { createAsyncThunk } from '@reduxjs/toolkit';
import type { AppDispatch, AppStore, RootState } from './store';
import type { AppDispatch, AppStore, ExtraArguments, RootState } from './store';
export const createAppAsyncThunk = createAsyncThunk.withTypes<{
state: RootState;
dispatch: AppDispatch;
extra: ExtraArguments;
}>();
export const useStore = useReduxStore.withTypes<AppStore>();

View File

@@ -4,6 +4,7 @@ import {
createListenerMiddleware,
isRejected,
} from '@reduxjs/toolkit';
import type { QueryClient } from '@tanstack/react-query';
import {
name as accountsSliceName,
@@ -77,27 +78,28 @@ notifyOnRejectedActionsMiddleware.startListening({
},
});
export const store = configureStore({
reducer: rootReducer,
middleware: getDefaultMiddleware =>
getDefaultMiddleware({
// TODO: Fix this in a separate PR. Remove non-serializable states in the store.
serializableCheck: false,
}).prepend(notifyOnRejectedActionsMiddleware.middleware),
});
export function configureAppStore() {
export function configureAppStore({
queryClient,
}: {
queryClient: QueryClient;
}) {
return configureStore({
reducer: rootReducer,
middleware: getDefaultMiddleware =>
getDefaultMiddleware({
// TODO: Fix this in a separate PR. Remove non-serializable states in the store.
serializableCheck: false,
thunk: {
extraArgument: { queryClient } as ExtraArguments,
},
}).prepend(notifyOnRejectedActionsMiddleware.middleware),
});
}
export type AppStore = typeof store;
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export type GetRootState = typeof store.getState;
export type AppStore = ReturnType<typeof configureAppStore>;
export type RootState = ReturnType<AppStore['getState']>;
export type AppDispatch = AppStore['dispatch'];
export type GetRootState = AppStore['getState'];
export type ExtraArguments = {
queryClient: QueryClient;
};

View File

@@ -0,0 +1,6 @@
---
category: Bugfixes
authors: [joel-jeremy]
---
Fix react query cache not being cleared when switching budgets