diff --git a/package-lock.json b/package-lock.json
index 8bc3036..a249d84 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,6 +14,7 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"recharts": "^2.12.1",
+ "use-debounce": "^10.0.4",
"zustand": "^4.5.1"
},
"devDependencies": {
@@ -4172,6 +4173,18 @@
"punycode": "^2.1.0"
}
},
+ "node_modules/use-debounce": {
+ "version": "10.0.4",
+ "resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-10.0.4.tgz",
+ "integrity": "sha512-6Cf7Yr7Wk7Kdv77nnJMf6de4HuDE4dTxKij+RqE9rufDsI6zsbjyAxcH5y2ueJCQAnfgKbzXbZHYlkFwmBlWkw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 16.0.0"
+ },
+ "peerDependencies": {
+ "react": "*"
+ }
+ },
"node_modules/use-sync-external-store": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz",
diff --git a/package.json b/package.json
index f6ecd87..27e6453 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,7 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"recharts": "^2.12.1",
+ "use-debounce": "^10.0.4",
"zustand": "^4.5.1"
},
"devDependencies": {
diff --git a/src/App.tsx b/src/App.tsx
index 6a2694d..c5389cb 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -41,7 +41,7 @@ export default function App() {
diff --git a/src/components/AddAssetModal.tsx b/src/components/AddAssetModal.tsx
index eb46ebe..5850386 100644
--- a/src/components/AddAssetModal.tsx
+++ b/src/components/AddAssetModal.tsx
@@ -1,5 +1,6 @@
import { Search, X } from "lucide-react";
-import React, { useCallback, useEffect, useRef, useState } from "react";
+import React, { useState } from "react";
+import { useDebouncedCallback } from "use-debounce";
import { getHistoricalData, searchAssets } from "../services/yahooFinanceService";
import { usePortfolioStore } from "../store/portfolioStore";
@@ -13,26 +14,6 @@ export const AddAssetModal = ({ onClose }: { onClose: () => void }) => {
addAsset: state.addAsset,
dateRange: state.dateRange,
}));
- const searchTimeoutRef = useRef
();
-
- const debouncedSearch = useCallback((query: string) => {
- if (searchTimeoutRef.current) {
- clearTimeout(searchTimeoutRef.current);
- }
-
- searchTimeoutRef.current = setTimeout(() => {
- handleSearch(query);
- }, 500);
- }, []);
-
- // Cleanup timeout on unmount
- useEffect(() => {
- return () => {
- if (searchTimeoutRef.current) {
- clearTimeout(searchTimeoutRef.current);
- }
- };
- }, []);
const handleSearch = async (query: string) => {
if (query.length < 2) return;
@@ -47,6 +28,8 @@ export const AddAssetModal = ({ onClose }: { onClose: () => void }) => {
}
};
+ const debouncedSearch = useDebouncedCallback(handleSearch, 750);
+
const handleAssetSelect = async (asset: Asset) => {
setLoading(true);
try {
diff --git a/src/components/PortfolioChart.tsx b/src/components/PortfolioChart.tsx
index 50c7817..5d8eb2a 100644
--- a/src/components/PortfolioChart.tsx
+++ b/src/components/PortfolioChart.tsx
@@ -4,9 +4,12 @@ import { useCallback, useMemo, useState } from "react";
import {
CartesianGrid, Legend, Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis
} from "recharts";
+import { useDebouncedCallback } from "use-debounce";
import { useDarkMode } from "../providers/DarkModeProvider";
+import { getHistoricalData } from "../services/yahooFinanceService";
import { usePortfolioStore } from "../store/portfolioStore";
+import { DateRange } from "../types";
import { calculatePortfolioValue } from "../utils/calculations/portfolioValue";
import { DateRangePicker } from "./DateRangePicker";
@@ -45,12 +48,25 @@ export const PortfolioChart = () => {
const [hideAssets, setHideAssets] = useState(false);
const [hiddenAssets, setHiddenAssets] = useState>(new Set());
const { isDarkMode } = useDarkMode();
- const { assets, dateRange, updateDateRange } = usePortfolioStore((state) => ({
+ const { assets, dateRange, updateDateRange, updateAssetHistoricalData } = usePortfolioStore((state) => ({
assets: state.assets,
dateRange: state.dateRange,
updateDateRange: state.updateDateRange,
+ updateAssetHistoricalData: state.updateAssetHistoricalData,
}));
+ const fetchHistoricalData = useCallback(
+ async (startDate: string, endDate: string) => {
+ assets.forEach(async (asset) => {
+ const historicalData = await getHistoricalData(asset.symbol, startDate, endDate);
+ updateAssetHistoricalData(asset.id, historicalData);
+ });
+ }, [assets, updateAssetHistoricalData]);
+
+ const debouncedFetchHistoricalData = useDebouncedCallback(fetchHistoricalData, 1500, {
+ maxWait: 5000,
+ });
+
const assetColors: Record = useMemo(() => {
const usedColors = new Set();
return assets.reduce((colors, asset) => {
@@ -176,14 +192,20 @@ export const PortfolioChart = () => {
);
}, [hideAssets, hiddenAssets, toggleAsset, toggleAllAssets]);
+ const handleUpdateDateRange = useCallback((newRange: DateRange) => {
+ updateDateRange(newRange);
+
+ debouncedFetchHistoricalData(newRange.startDate, newRange.endDate);
+ }, [updateDateRange, debouncedFetchHistoricalData]);
+
const ChartContent = useCallback(() => (
<>
updateDateRange({ ...dateRange, startDate: date })}
- onEndDateChange={(date) => updateDateRange({ ...dateRange, endDate: date })}
+ onStartDateChange={(date) => handleUpdateDateRange({ ...dateRange, startDate: date })}
+ onEndDateChange={(date) => handleUpdateDateRange({ ...dateRange, endDate: date })}
/>