Почему мне нужно обновлять страницу, чтобы увидеть изменения в пользовательском интерфейсе после обновления списка наблюJavascript

Форум по Javascript
Ответить
Anonymous
 Почему мне нужно обновлять страницу, чтобы увидеть изменения в пользовательском интерфейсе после обновления списка наблю

Сообщение Anonymous »

Я создаю стандартную панель мониторинга, используя Next.js 13 (App Router) с серверными и клиентскими компонентами React. У меня есть функция «список наблюдения», с помощью которой пользователи могут добавлять/удалять акции, и изменения корректно сохраняются в моей базе данных.
Однако после нажатия кнопки «Добавить/Удалить» пользовательский интерфейс не сразу отражает обновленное состояние. Мне нужно обновить страницу, чтобы увидеть звездочку или обновление кнопки «Добавить/удалить из списка наблюдения».
Репозиторий Github: https://github.com/Thodoris-Diamantidis ... -dashboard
Вот упрощенная версия моей настройки:
WatchlistButton (клиентский компонент):

Код: Выделить всё

"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"}

);
}

И компонент SearchCommand (клиент):

Код: Выделить всё

"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]
)}



);
}
Проблема:
  • При нажатии кнопки база данных обновляется правильно.
  • Но пользовательский интерфейс (значок звездочки/кнопка) не отражает изменения, пока я не перезагружаю страницу.
Что я могу сделать? попробовал:
  • Использование useState и onChange для распространения изменения на родительский элемент.
  • Сделать кнопку управляемым компонентом, полагаясь на isInWatchlist из родительского элемента.
  • Отказ от вызовов API.
Вопрос:
Почему мой пользовательский интерфейс не обновляется сразу, хотя обновление базы данных работает? Как я могу обновить кнопку и результаты поиска без необходимости обновлять страницу?
Я подозреваю, что это связано с компонентами сервера и клиентскими компонентами в Next.js 13, но я не уверен, какой подход лучше всего.
Любые рекомендации о том, как правильно синхронизировать состояние клиента и состояние сервера для этого шаблона, будут оценены!

Подробнее здесь: https://stackoverflow.com/questions/798 ... -watchlist
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Javascript»