Skip to content

Commit b1d6fcf

Browse files
kitfunsoclaude
andcommitted
Fix currency selector sync issue between homepage and calculators
- CurrencySelector: Use useLayoutEffect for synchronous sync before paint - CurrencySelector: Use refs to avoid stale closures in event handlers - useLocalStorage: Auto-sync currency field with global preference when loading saved state, so calculators respect homepage currency selection while preserving other saved values Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent c436450 commit b1d6fcf

2 files changed

Lines changed: 43 additions & 8 deletions

File tree

src/components/ui/calculator/CurrencySelector.tsx

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
import { useEffect } from 'preact/hooks';
1+
import { useEffect, useLayoutEffect, useRef } from 'preact/hooks';
22
import { CURRENCY_OPTIONS, type Currency } from '../../../lib/regions';
33

44
const STORAGE_KEY = 'boring-math-currency';
55

6+
// Use useLayoutEffect on client, useEffect on server
7+
const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
8+
69
export interface CurrencySelectorProps {
710
/** Currently selected currency */
811
value: Currency;
@@ -30,24 +33,36 @@ export interface CurrencySelectorProps {
3033
* ```
3134
*/
3235
export function CurrencySelector({ value, onChange, className = '' }: CurrencySelectorProps) {
33-
// Sync with global currency on mount and listen for changes
36+
// Keep refs to avoid stale closures in event handlers
37+
const valueRef = useRef(value);
38+
const onChangeRef = useRef(onChange);
39+
40+
// Update refs when props change
3441
useEffect(() => {
35-
// Load initial value from localStorage
42+
valueRef.current = value;
43+
onChangeRef.current = onChange;
44+
}, [value, onChange]);
45+
46+
// Sync with global currency on mount (synchronously before paint)
47+
useIsomorphicLayoutEffect(() => {
3648
const stored = localStorage.getItem(STORAGE_KEY);
3749
if (stored && ['USD', 'GBP', 'EUR'].includes(stored) && stored !== value) {
3850
onChange(stored as Currency);
3951
}
52+
}, []); // Only on mount
4053

41-
// Listen for changes from other calculators
54+
// Listen for changes from other calculators
55+
useEffect(() => {
4256
const handler = (e: CustomEvent<{ currency: Currency }>) => {
43-
if (e.detail.currency !== value) {
44-
onChange(e.detail.currency);
57+
const newCurrency = e.detail.currency;
58+
if (newCurrency && newCurrency !== valueRef.current) {
59+
onChangeRef.current(newCurrency);
4560
}
4661
};
4762

4863
window.addEventListener('currencyChange', handler as EventListener);
4964
return () => window.removeEventListener('currencyChange', handler as EventListener);
50-
}, []);
65+
}, []); // Stable handler using refs
5166

5267
const handleChange = (newCurrency: Currency) => {
5368
// Save to localStorage

src/hooks/useLocalStorage.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,15 @@
33
*
44
* Persists state to localStorage and syncs across tabs.
55
* Perfect for saving calculator inputs so users don't lose their work.
6+
*
7+
* When loading saved state that has a 'currency' field, it will automatically
8+
* sync with the global currency preference from 'boring-math-currency'.
69
*/
710

811
import { useState, useEffect, useCallback } from 'preact/hooks';
12+
import { type Currency, getInitialCurrency } from '../lib/regions';
13+
14+
const CURRENCY_STORAGE_KEY = 'boring-math-currency';
915

1016
export function useLocalStorage<T>(
1117
key: string,
@@ -20,7 +26,21 @@ export function useLocalStorage<T>(
2026
try {
2127
const stored = localStorage.getItem(key);
2228
if (stored) {
23-
return JSON.parse(stored);
29+
const parsed = JSON.parse(stored) as T;
30+
31+
// If the saved state has a currency field, sync it with the global preference
32+
if (parsed && typeof parsed === 'object' && 'currency' in parsed) {
33+
const globalCurrency = getInitialCurrency();
34+
const savedCurrency = (parsed as { currency: Currency }).currency;
35+
36+
// If global currency differs from saved, update the currency field
37+
// but keep all other saved values
38+
if (globalCurrency !== savedCurrency) {
39+
return { ...parsed, currency: globalCurrency };
40+
}
41+
}
42+
43+
return parsed;
2444
}
2545
} catch (error) {
2646
console.warn(`Error reading localStorage key "${key}":`, error);

0 commit comments

Comments
 (0)