Невозможно изменить состояние кнопки диалогового окна onClick

Я работаю над приложением Next.js, и в одном из компонентов у меня есть DropdownMenu, где один из его элементов запускает DialogAlert (оба компонента Shadcn). Я пытаюсь контролировать открытое состояние диалога с помощью setIsDialogOpen, но это работает только тогда, когда перед ним есть синхронная функция (как в примере с кнопкой подтверждения). В противном случае это не сработает, если я просто позвоню setIsDialogOpen(false) из действия кнопки отмены.

Вот код, воспроизводящий проблему:

"use client";
import { useState } from "react";
import {
 AlertDialog,
 AlertDialogAction,
 AlertDialogCancel,
 AlertDialogContent,
 AlertDialogDescription,
 AlertDialogFooter,
 AlertDialogHeader,
 AlertDialogTitle,
 AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import {
 DropdownMenu,
 DropdownMenuContent,
 DropdownMenuItem,
 DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Loader2 } from "lucide-react";

export default function Home() {
 const [isDialogOpen, setIsDialogOpen] = useState(false);
 const [isLoading, setIsLoading] = useState(false);

 const handleCancelDelete = () => {
   setIsDialogOpen(false);
 };

 const handleDelete = async () => {
   setIsLoading(true);
   // Perform your delete operation here
   await new Promise((resolve) => setTimeout(resolve, 2000)); // Simulating an async operation
   setIsLoading(false);
 };

 return (
   <div>
     <DropdownMenu>
       <DropdownMenuTrigger>Dropdown</DropdownMenuTrigger>
       <DropdownMenuContent>
         <DropdownMenuItem>Action</DropdownMenuItem>
         <DropdownMenuItem
           onClick = {() => setIsDialogOpen(true)}
           onSelect = {(event) => event.preventDefault()}
         >
           <AlertDialog open = {isDialogOpen} onOpenChange = {setIsDialogOpen}>
             <AlertDialogTrigger>Delete</AlertDialogTrigger>
             <AlertDialogContent>
               <AlertDialogHeader>
                 <AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
               </AlertDialogHeader>
               <AlertDialogFooter>
                 <AlertDialogCancel
                   onClick = {() => {
                     alert("onClick works");
                     setIsDialogOpen(false);
                   }}
                 >
                   Cancel
                 </AlertDialogCancel>
                 {isLoading ? (
                   <Loader2 className = "animate-spin" />
                 ) : (
                   <AlertDialogAction
                     onClick = {async (e) => {
                       e.preventDefault(); // Prevent default action that closes the dialog
                       await handleDelete();
                       setIsDialogOpen(false);
                     }}
                   >
                     Continue
                   </AlertDialogAction>
                 )}
               </AlertDialogFooter>
             </AlertDialogContent>
           </AlertDialog>
         </DropdownMenuItem>
       </DropdownMenuContent>
     </DropdownMenu>
   </div>
 );
}

Вот ссылка CodeSandbox, чтобы воспроизвести проблему: https://codesandbox.io/p/devbox/cranky-montalcini-4432z5?embed=1&file=%2Fapp%2Fpage.tsx%3A38%2C20

В вашей ссылке CodeSandbox, когда я нажимаю кнопку отмены, диалоговое окно закрывается. Разве это не ожидаемый результат?

Robert 19.06.2024 08:20

Код в песочнице, кажется, работает нормально. Можете ли вы отредактировать, чтобы прояснить проблему и каковы ваши шаги по воспроизведению для ее создания?

Drew Reese 19.06.2024 09:37

Извините, ребята, когда я нашел решение, я отредактировал код песочницы, чтобы проверить его, но забыл сделать то же самое здесь,

Ăżiž 19.06.2024 10:37
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
3
61
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Проблема, с которой я столкнулся, связана с распространением событий в React. Когда вы нажимаете кнопку «Отмена», событие клика распространяется до родительского компонента (в данном случае DropdownMenuItem).

Чтобы это исправить, мне пришлось остановить распространение события, вызвав event.stopPropagation() в обработчике onClick кнопки «Отмена». Вот обновленный код этой части:

<AlertDialogCancel
  onClick = {(e) => {
    e.stopPropagation(); // Add this line to stop event propagation
    setIsDialogOpen(false);
  }}
>
  Cancel
</AlertDialogCancel>
Ответ принят как подходящий

Возможно, вы также хотите закрыть раскрывающийся список при нажатии кнопки отмены в диалоговом окне предупреждения. Для этого вам необходимо преобразовать раскрывающийся компонент из неконтролируемого в контролируемый, передав реквизиты open и onChangeOpen в раскрывающийся компонент.

export default function Home() {
  const [isDialogOpen, setIsDialogOpen] = useState(false);
  const [isLoading, setIsLoading] = useState(false);

  const handleCancelDelete = () => {
    setIsDialogOpen(false);
  };

  const handleDelete = async () => {
    setIsLoading(true);
    // Perform your delete operation here
    await new Promise((resolve) => setTimeout(resolve, 2000)); // Simulating an async operation
    setIsLoading(false);
  };

  const [isDropDownOpen, setIsDropDownOpen] = useState(false);

  return (  
    <div> 
      <DropdownMenu open = {isDropDownOpen} onOpenChange = {() => {
        setIsDropDownOpen(!isDropDownOpen);
      }}>
        <DropdownMenuTrigger>Dropdown</DropdownMenuTrigger>
        <DropdownMenuContent>
          <DropdownMenuItem>Action</DropdownMenuItem>
          <DropdownMenuItem
            onClick = {() => setIsDialogOpen(true)}
            onSelect = {(event) => event.preventDefault()}
          >
            <AlertDialog open = {isDialogOpen} onOpenChange = {setIsDialogOpen}>
              <AlertDialogTrigger>Delete</AlertDialogTrigger>
              <AlertDialogContent>
                <AlertDialogHeader>
                  <AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
                </AlertDialogHeader>
                <AlertDialogFooter>
                  <AlertDialogCancel
                    onClick = {(e) => {
                      e.stopPropagation(); // Add this line to stop event propagation
                      setIsDropDownOpen(false);
                      setIsDialogOpen(false);
                    }}
                  >
                    Cancel
                  </AlertDialogCancel>
                  {isLoading ? (
                    <Loader2 className = "animate-spin" />
                  ) : (
                    <AlertDialogAction
                      onClick = {async (e) => {
                        e.preventDefault(); // Prevent default action that closes the dialog
                        await handleDelete();
                        setIsDialogOpen(false);
                      }}
                    >
                      Continue
                    </AlertDialogAction>
                  )}
                </AlertDialogFooter>
              </AlertDialogContent>
            </AlertDialog>
          </DropdownMenuItem>
        </DropdownMenuContent>
      </DropdownMenu>
    </div>
  );
}

Приятно, спасибо чувак! мне это действительно нужно

Ăżiž 19.06.2024 11:20

Реализация предлагаемого решения вызвала проблему, из-за которой все компоненты в моем приложении стали неактивными и зависли после запуска раскрывающегося меню, запуска действия удаления, его отмены и последующей прокрутки вниз. Обратите внимание, что у меня есть прокручиваемый компонент с несколькими компонентами, использующими это раскрывающееся меню. Есть ли у вас какие-нибудь мысли по этому поводу?

Ăżiž 19.06.2024 20:08

Такой пункт меню, как «Действие», не будет доступен для кликов, вам необходимо явно установить DropDown(false) onClick для всех остальных пунктов меню.

Mudassar Malik 19.06.2024 21:16

Другие вопросы по теме