Next.js 13调用useState() setter触发loading.tsx及失焦问题解决
问题描述
我有一个使用useState() Hook的客户端组件,包含一个简单文本输入框。每当修改文本触发onChange事件时,页面会显示loading.tsx中定义的加载UI,之后才更新界面,但文本框会失去焦点。请问需要如何修改,才能让UI刷新时不显示加载UI,同时保持文本框的焦点?
文件夹结构
- app - layout.tsx - products - page.tsx - loading.tsx - [id] - page.tsx (上述提及的页面)
相关代码
app/products/[id]/page.tsx
"use client"; import { useState } from "react"; export default async function ProductDetail({ params, }: { params: { id: string }; }) { const product = { id: "123", name: "Testprodukt" }; const [name, setName] = useState(product.name); return ( <div> <h1>Edit product</h1> <div> <p>Product name</p> <input type="text" placeholder="Product name" value={name} onChange={(e) => setName(e.target.value)} /> </div> </div> ); }
app/products/loading.tsx
import LoadingIndicator from "@/components/LoadingIndicator"; export default function Loading() { return ( <div> <LoadingIndicator /> </div> ); }
app/layout.tsx
import Navbar from "@/components/Navbar"; import "./globals.css"; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html lang="en"> <body> <div> <Navbar /> <div>{children}</div> </div> </body> </html> ); }
解决方案
问题根源
你的ProductDetail组件标记了async,即便没有实际异步操作,Next.js + React 仍会将其视为Suspense 组件,状态更新时会触发路由级别的loading.tsx加载UI。同时,异步组件的重新渲染可能导致输入框DOM被销毁重建,引发焦点丢失。
修改步骤
移除组件的
async关键字
将ProductDetail从异步函数改为普通函数,避免触发Suspense相关的加载逻辑:"use client"; import { useState } from "react"; // 去掉async关键字 export default function ProductDetail({ params, }: { params: { id: string }; }) { const product = { id: "123", name: "Testprodukt" }; const [name, setName] = useState(product.name); return ( <div> <h1>Edit product</h1> <div> <p>Product name</p> <input type="text" placeholder="Product name" value={name} onChange={(e) => setName(e.target.value)} /> </div> </div> ); }(可选)添加稳定key强化焦点留存
如果后续仍出现焦点丢失问题,给输入框添加固定key,确保React识别它是同一个DOM元素:<input key={`product-name-${params.id}`} type="text" placeholder="Product name" value={name} onChange={(e) => setName(e.target.value)} />
效果说明
- 移除
async后,组件变为纯客户端状态管理,状态更新仅触发局部DOM更新,不会触发路由的loading.tsx。 - 输入框DOM不会被销毁重建,焦点会自然保持在输入框内。
内容的提问来源于stack exchange,提问作者LarsDev
相关产品推荐
相关产品推荐

