This document describes the design for sharing the React Query client between the main Wealthfolio application and addons, enabling automatic cache invalidation and data synchronization.
Instead of each addon creating its own QueryClient, addons now share the main application's QueryClient:
Before:
// Each addon had its own QueryClient
const queryClient = new QueryClient({
/* config */
});After:
// Addons use the shared QueryClient from main app
const sharedQueryClient = ctx.api.query.getClient();The main app exposes its QueryClient globally:
// In App.tsx
const [queryClient] = useState(
() =>
new QueryClient({
/* config */
}),
);
(window as any).__wealthfolio_query_client__ = queryClient;Both the main app and addons use the same query keys to ensure cache consistency:
// Shared in @wealthfolio/addon-sdk
export const QueryKeys = {
GOALS: "goals",
GOALS_ALLOCATIONS: "goals_allocations",
ACCOUNTS: "accounts",
// ... other keys
} as const;Addons have access to query management functions:
interface QueryAPI {
getClient(): QueryClient;
invalidateQueries(queryKey: string | string[]): void;
refetchQueries(queryKey: string | string[]): void;
}- Single source of truth for all cached data
- No data duplication between main app and addons
- Consistent cache invalidation strategies
- When main app updates data, addons automatically see changes
- No need for manual refresh or polling mechanisms
- Real-time updates across the entire application
- Single network request per data type
- Shared cache reduces memory usage
- Efficient data fetching and caching
- Addons can listen for data change events
- Automatic cache invalidation on data mutations
- Reactive UI updates without manual intervention
The goal progress tracker addon demonstrates this pattern:
// Simple hook using shared query keys - no event listeners needed
export function useGoals({ ctx, enabled = true }: UseGoalsOptions) {
return useQuery<Goal[]>({
queryKey: [QueryKeys.GOALS], // Shared query key
queryFn: async () => {
const data = await ctx.api.goals.getAll();
return data || [];
},
enabled: enabled && !!ctx.api,
staleTime: 10 * 60 * 1000, // 10 minutes
gcTime: 30 * 60 * 1000, // 30 minutes
retry: 3,
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
});
}const AddonWrapper = () => {
const sharedQueryClient = ctx.api.query.getClient();
return (
<QueryClientProvider client={sharedQueryClient}>
<AddonComponent ctx={ctx} />
</QueryClientProvider>
);
};- User creates a goal in Settings → Goals page
- Main app updates the goal via mutation
- Main app invalidates
QueryKeys.GOALScache automatically (via React Query mutation) - Goal Progress Tracker addon automatically receives updated data (shared cache)
- Addon UI re-renders with fresh data
With the shared query client approach, cache invalidation happens automatically:
- Main app mutations use React Query's built-in invalidation
- Shared cache means invalidation affects all components (main app + addons)
- No manual event handling required in addon hooks
- Automatic synchronization across the entire application
Always import and use QueryKeys from the addon SDK:
import { QueryKeys } from "@wealthfolio/addon-sdk";Set up event listeners in your hooks to automatically invalidate cache when data changes:
useEffect(() => {
const unlisten = await ctx.api.events.goals.onCreate(() => {
ctx.api.query.invalidateQueries([QueryKeys.GOALS]);
});
return unlisten;
}, [ctx]);Always use the shared QueryClient for consistency:
const queryClient = ctx.api.query.getClient();Use descriptive and consistent cache keys that match the main app's patterns:
queryKey: [QueryKeys.GOALS];
queryKey: [QueryKeys.latestValuations, accountIds];- Typed Event Payloads: Add strong typing for event payloads
- Granular Invalidation: More specific cache invalidation strategies
- Optimistic Updates: Support for optimistic UI updates
- Cache Persistence: Shared cache persistence strategies
- Cache Analytics: Monitoring and debugging tools for cache behavior
This design provides a robust foundation for data synchronization between the main application and addons, ensuring a seamless user experience across all components of the Wealthfolio ecosystem.