Warum muss ich meine Seite aktualisieren, um nach der Aktualisierung der Beobachtungsliste Änderungen in der BenutzerobeJavaScript

Javascript-Forum
Anonymous
 Warum muss ich meine Seite aktualisieren, um nach der Aktualisierung der Beobachtungsliste Änderungen in der Benutzerobe

Post by Anonymous »

Ich erstelle ein Standard-Dashboard mit Next.js 13 (App Router) mit React Server- und Client-Komponenten. Ich habe eine „Beobachtungslisten“-Funktion, mit der Benutzer Aktien hinzufügen/entfernen können, und die Änderungen werden korrekt in meiner Datenbank gespeichert.
Nach dem Klicken auf die Schaltfläche „Hinzufügen/Entfernen“ wird die Benutzeroberfläche jedoch nicht sofort aktualisiert. Ich muss die Seite aktualisieren, um den Sternschalter oder die Aktualisierung der Schaltfläche „Zur Beobachtungsliste hinzufügen/entfernen“ zu sehen.
Github-Repository
Hier ist eine vereinfachte Version meines Setups:
WatchlistButton (Client-Komponente):

Code: Select all

"use client";

import {
addToWatchlist,
removeFromWatchlist,
} from "@/lib/actions/watchlist.actions";
import { Star, Trash2 } from "lucide-react";
import { useCallback, useEffect, useRef, useState } from "react";
import { toast } from "sonner";

type WatchlistButtonProps = {
symbol: string;
company: string;
isInWatchlist: boolean;
type?: "button" | "icon";
showTrashIcon?: boolean;
onChange?: (next: boolean) => void;
};

export default function WatchlistButton({
symbol,
company,
isInWatchlist,
type = "icon",
showTrashIcon = false,
onChange,
}: WatchlistButtonProps) {
const [added, setAdded] = useState(!!isInWatchlist);
const debounceRef = useRef(null);
const lastIntent = useRef(isInWatchlist);

useEffect(() => {
setAdded(isInWatchlist);
lastIntent.current = isInWatchlist;
}, [isInWatchlist]);

const commit = useCallback(
async (next: boolean) => {
try {
if (next) {
await addToWatchlist(symbol, company);
toast.success(`${symbol} added to watchlist`);
} else {
await removeFromWatchlist(symbol);
toast.success(`${symbol} removed from watchlist`);
}

onChange?.(next);
} catch {
setAdded(lastIntent.current);
toast.error("Something went wrong");
}
},
[symbol, company]
);

const toggle = (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();

const next = !added;
setAdded(next);
lastIntent.current = next;

if (debounceRef.current) clearTimeout(debounceRef.current);
debounceRef.current = setTimeout(() => commit(next), 300);
};

const Icon = showTrashIcon && added ? Trash2 : Star;

if (type === "icon") {
return (



);
}

return (

{showTrashIcon && added ?  : null}
{added ? "Remove from Watchlist"  : "Add to Watchlist"}

);
}

Und SearchCommand-Komponente (Client):

Code: Select all

"use client";

import { Button } from "@/components/ui/button";
import {
CommandDialog,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command";
import { useDebounce } from "@/hooks/useDebounce";
import { searchStocks } from "@/lib/actions/finnhub.actions";
import { SearchCommandProps, StockWithWatchlistStatus } from "@/types/crypto";
import { TrendingUp } from "lucide-react";
import Link from "next/link";
import { useEffect, useState } from "react";
import WatchlistButton from "./WatchlistButton";
import { useUser } from "@/lib/UserContext";

export default function SearchCommand({
renderAs = "button",
label = "Add stock",
initialStocks,
}: SearchCommandProps) {
const { user: currentUser } = useUser();
const [open, setOpen] = useState(false);
const [searchTerm, setSearchTerm] = useState("");
const [loading, setLoading] = useState(false);
const [stocks, setStocks] =
useState(initialStocks);

const isSearchMode = !!searchTerm.trim();
const displayStocks = isSearchMode ? stocks : stocks?.slice(0, 10);

const [watchlistMap, setWatchlistMap] = useState({});
// Populate the map whenever initialStocks change
useEffect(() => {
const map: Record = {};
initialStocks.forEach((s) => {
map[s.symbol] = s.isInWatchlist;
});
setWatchlistMap(map);
setStocks(initialStocks);
}, [initialStocks]);

useEffect(() => {
const onKeyDown = (e: KeyboardEvent) => {
if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
e.preventDefault();
setOpen((v) => !v);
}
};
window.addEventListener("keydown", onKeyDown);
return () => window.removeEventListener("keydown", onKeyDown);
}, []);

const handleSearch = async () => {
if (!isSearchMode) return setStocks(initialStocks);
setLoading(true);
try {
const results = await searchStocks(searchTerm.trim());
setStocks(results);
} catch {
setStocks([]);
} finally {
setLoading(false);
}
};

const debouncedSearch = useDebounce(handleSearch, 400);

useEffect(() => {
debouncedSearch();
}, [searchTerm]);

const handleSelectStock = () => {
setOpen(false);
setSearchTerm("");
setStocks(initialStocks);
};

//If the user closes the Search with escape remove any filters
useEffect(() => {
if (!open) {
setSearchTerm("");
setStocks(initialStocks);
}
}, [open]);

return (

{renderAs === "text" ? (
 setOpen(true)}>{label}
) : (
 setOpen(true)}>{label}
)}



{loading ? (
Loading stocks...
) : displayStocks?.length === 0 ? (

{isSearchMode ? "No results found" : "No stocks available"}

) : (
[list]

{isSearchMode ? "Search results" : "Popular stocks"}
{` `}({displayStocks?.length || 0})

{displayStocks?.map((stock, i) =>  (
[*]                  key={`${stock.symbol}-${i}`} //Unique key now
className="rounded-none my-3 px-1 w-full data-[selected=true]:bg-gray-600"
>



{stock.name}


{stock.symbol} | {stock.exchange} | {stock.type}

{currentUser && (
 {
setWatchlistMap((prev) => ({
...prev,
[stock.symbol]: newState,
}));
}}
/>
)}


))}
[/list]
)}



);
}
Problem:
  • Durch Klicken auf die Schaltfläche wird die Datenbank korrekt aktualisiert.
  • Aber die Benutzeroberfläche (Sternsymbol/Schaltfläche) spiegelt die Änderung nicht wider, es sei denn, ich lade die Seite neu.
Was ich versucht:
  • Verwenden von useState und onChange, um die Änderung an das übergeordnete Element weiterzugeben.
  • Die Schaltfläche zu einer kontrollierten Komponente machen, indem man sich auf isInWatchlist vom übergeordneten Element verlässt.
  • Entprellen der API-Aufrufe.
Frage:
Warum wird meine Benutzeroberfläche nicht sofort aktualisiert, obwohl die Datenbankaktualisierung funktioniert? Wie kann ich dafür sorgen, dass die Schaltfläche und die Suchergebnisse aktualisiert werden, ohne dass die Seite aktualisiert werden muss?
Ich vermute, dass es mit Serverkomponenten vs. Clientkomponenten in Next.js 13 zusammenhängt, bin mir aber nicht sicher, was der beste Ansatz ist.
Jede Anleitung, wie man Client- und Serverstatus für dieses Muster richtig synchronisiert, wäre dankbar!

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post