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

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被销毁重建,引发焦点丢失。

修改步骤

  1. 移除组件的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>
      );
    }
    
  2. (可选)添加稳定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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 09:52:55