-
Live Prices
-
- {isConnected ? 'WebSocket Connected' : 'WebSocket Disconnected'}
+
Live Prices
+
+ {isConnected ? "WebSocket Connected" : "WebSocket Disconnected"}
{error &&
Error: {error}
}
- {data?.map((p: PriceData) => (
-
- {p.symbol}: {p.price}
-
- ))}
+
+
+ {initialAssets.map((id: string) => (
+
+ ))}
+
)
}
-export default memo(LivePrices);
\ No newline at end of file
+export default memo(LivePrices)
\ No newline at end of file
diff --git a/src/app/components/providers/QueryProvider.tsx b/src/app/components/providers/QueryProvider.tsx
index 3384db2..9eead3c 100644
--- a/src/app/components/providers/QueryProvider.tsx
+++ b/src/app/components/providers/QueryProvider.tsx
@@ -1,3 +1,5 @@
+"use client"
+
import { QueryClientProvider } from '@tanstack/react-query'
import { queryClient } from '../../lib/queryClient'
diff --git a/src/app/globals.css b/src/app/globals.css
index a3fcba3..a3121fc 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -3,6 +3,8 @@
:root {
--background: #ffffff;
--foreground: #171717;
+ --font-geist-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+ --font-geist-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
}
/* Dark theme — class applied by next-themes */
diff --git a/src/app/hooks/useAssetPrice.ts b/src/app/hooks/useAssetPrice.ts
new file mode 100644
index 0000000..8ab41b9
--- /dev/null
+++ b/src/app/hooks/useAssetPrice.ts
@@ -0,0 +1,39 @@
+"use client"
+
+import { useEffect, useState } from "react"
+import { useSocketActions, useSocketData } from "../components/providers/SocketProvider"
+
+interface AssetPrice {
+ price?: number
+ timestamp?: number
+}
+
+/**
+ * Subscribe to a single asset and expose a localized price state.
+ * This hook only updates its internal state when the incoming WS tick
+ * targets `assetId`, avoiding re-renders when unrelated assets change.
+ */
+export function useAssetPrice(assetId: string): AssetPrice {
+ const { subscribeToAsset, unsubscribeFromAsset } = useSocketActions()
+ const { lastUpdate } = useSocketData()
+
+ const [state, setState] = useState
({ price: undefined, timestamp: undefined })
+
+ // Subscribe once on mount, and unsubscribe on unmount.
+ useEffect(() => {
+ subscribeToAsset(assetId)
+ return () => {
+ unsubscribeFromAsset(assetId)
+ }
+ }, [assetId, subscribeToAsset, unsubscribeFromAsset])
+
+ // Update local state only when the global `lastUpdate` refers to our asset.
+ useEffect(() => {
+ if (!lastUpdate) return
+ if (lastUpdate.assetPair === assetId) {
+ setState({ price: lastUpdate.price, timestamp: lastUpdate.timestamp })
+ }
+ }, [assetId, lastUpdate])
+
+ return state
+}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index aabe25f..73ef790 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,6 +1,5 @@
import "@/config/env";
import type { Metadata } from "next";
-import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import { ThemeProvider } from "./components/ThemeProvider";
import { ProgressBarProvider } from "./components/TopLoadingBar";
@@ -10,20 +9,6 @@ import { QueryProvider } from "./components/providers/QueryProvider";
import Script from "next/script";
import { SvgSprite } from "@/components/icons";
-const geistSans = Geist({
- variable: "--font-geist-sans",
- subsets: ["latin"],
- display: "swap",
- weight: ["400", "700"]
-});
-
-const geistMono = Geist_Mono({
- variable: "--font-geist-mono",
- subsets: ["latin"],
- display: "swap",
- weight: ["400", "700"]
-});
-
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",