You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NextJS14 App Router中Shadcn UI Dialog修改状态时消失问题

问题分析与解决

核心问题1:Dialog因组件重渲染丢失状态

你把SharedDialog定义在ActionMenu组件内部,每次isResultReturned状态变化时,ActionMenu会重新渲染,SharedDialog会被重新创建。Shadcn UI的Dialog内部维护了自己的打开/关闭状态,组件重新创建后这个状态会被重置,所以当你把isResultReturned设为true时,Dialog直接消失了。

核心问题2:状态更新的闭包陷阱

setTimeout里的console.log('check isResultReturned', isResultReturned)拿的是调用deleteNoteGroup时的旧状态值,并不是更新后的新值,所以你看到的日志会误导你以为状态没变化,但实际上setIsResultReturned(true)已经生效了。

修复步骤

  1. 移除内部嵌套的组件定义
    不要在组件内部嵌套定义组件,避免每次渲染都重新创建组件实例。修改后的ActionMenu代码如下:
// app/ActionItem.tsx
"use client"
import { useState } from 'react';
import {
    Dialog,
    DialogContent,
    DialogDescription,
    DialogHeader,
    DialogTitle,
    DialogTrigger,
    DialogFooter
} from "@/components/ui/dialog"

import { Button } from "@/components/ui/button"
import { Loader2 } from 'lucide-react';

export default function ActionMenu({ name }: { name: string }) {
    const [isResultReturned, setIsResultReturned] = useState(true)
    
    const deleteNoteGroup= () => {
        setIsResultReturned(false)
        setTimeout(() => {
            console.log('wait for 3 seconds')
            setIsResultReturned(true)
        }, 3000)
    }

    // 直接渲染Dialog,不再嵌套定义SharedDialog组件
    return (
        <Dialog>
            <DialogTrigger asChild>
                <Button variant="outline">Delete</Button>
            </DialogTrigger>
            <DialogContent>
                <DialogHeader>
                    <DialogTitle>Title</DialogTitle>
                    <DialogDescription>
                        Description
                    </DialogDescription>
                </DialogHeader>
                <DialogFooter>
                    <Button variant="destructive" onClick={deleteNoteGroup}>
                        {!isResultReturned && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
                        Delete
                    </Button>
                </DialogFooter>
            </DialogContent>
        </Dialog>
    )
}
  1. 解决闭包查看状态的问题(可选)
    如果需要在setTimeout里查看最新的isResultReturned状态,可以用useRef同步状态:
// 新增useRef和useEffect
const isResultReturnedRef = useRef(isResultReturned);
useEffect(() => {
    isResultReturnedRef.current = isResultReturned;
}, [isResultReturned]);

// 在setTimeout里查看最新状态
setTimeout(() => {
    console.log('wait for 3 seconds')
    setIsResultReturned(true)
    console.log('check isResultReturned', isResultReturnedRef.current)
}, 3000)

这样修改后,Dialog不会在状态更新时消失,状态设置也能正常工作。

内容的提问来源于stack exchange,提问作者richardoey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 17:56:12