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

Next.js 13表单切换页面时重复触发标签动画的解决方法

解决Next.js 13认证表单标签重复动画问题

针对你遇到的页面切换后返回表单,输入框重新聚焦触发标签重复动画的问题,核心原因是页面切换时组件卸载导致状态丢失,返回后重新挂载的组件会把输入框的聚焦事件当作首次交互处理。以下是两种可行的解决方案:

方案一:持久化交互状态与输入值

通过sessionStorage保存输入框的内容和是否被用户触摸过的状态,页面切换后再返回时,组件会读取之前的状态,避免重复触发动画。

示例组件代码:

'use client';

import { useState, useEffect } from 'react';

export default function AuthInput({ name, label, type = 'text' }) {
  // 从sessionStorage初始化状态,避免页面切换后丢失
  const [value, setValue] = useState(() => {
    return sessionStorage.getItem(`auth_form_${name}`) || '';
  });
  const [isTouched, setIsTouched] = useState(() => {
    return sessionStorage.getItem(`auth_form_${name}_touched`) === 'true';
  });

  // 实时同步状态到sessionStorage
  useEffect(() => {
    sessionStorage.setItem(`auth_form_${name}`, value);
  }, [name, value]);

  useEffect(() => {
    sessionStorage.setItem(`auth_form_${name}_touched`, isTouched.toString());
  }, [name, isTouched]);

  const handleFocus = () => {
    // 仅在首次交互时标记为已触摸
    if (!isTouched) setIsTouched(true);
  };

  const handleChange = (e) => {
    setValue(e.target.value);
  };

  // 标签激活条件:已触摸过 或 输入框有内容
  const isLabelActive = isTouched || value.length > 0;

  return (
    <div className="relative mb-5">
      <input
        type={type}
        name={name}
        value={value}
        onChange={handleChange}
        onFocus={handleFocus}
        className={`w-full px-4 py-2 border border-gray-300 rounded-lg outline-none transition-all duration-300 ${
          isLabelActive ? 'pt-6' : 'pt-2'
        }`}
      />
      <label
        className={`absolute left-4 top-2 text-gray-500 transition-all duration-300 ${
          isLabelActive ? 'top-0 text-xs font-medium' : 'text-base'
        }`}
      >
        {label}
      </label>
    </div>
  );
}

逻辑说明:

  • isTouched状态一旦被设为true就不会再变回false,确保用户交互过的输入框标签始终保持激活状态
  • 用sessionStorage持久化状态,页面切换后组件重新挂载时能读取到之前的交互记录,不会把返回后的聚焦当作首次操作

方案二:调整标签状态判断逻辑

不依赖focus事件直接触发动画,而是通过输入框内容和交互历史控制标签状态,同时利用useRef避免首次挂载外的重复触发。

示例组件代码:

'use client';

import { useState, useRef, useEffect } from 'react';

export default function AuthInput({ name, label, type = 'text' }) {
  const [value, setValue] = useState('');
  const [isLabelActive, setIsLabelActive] = useState(false);
  const firstMount = useRef(true);

  useEffect(() => {
    // 首次挂载时根据输入内容初始化标签状态
    setIsLabelActive(value.length > 0);
    firstMount.current = false;
  }, []);

  const handleFocus = () => {
    // 仅首次聚焦时触发动画
    if (!firstMount.current) return;
    setIsLabelActive(true);
  };

  const handleBlur = () => {
    // 失焦时仅当输入为空才恢复标签状态
    if (value.length === 0) setIsLabelActive(false);
  };

  const handleChange = (e) => {
    setValue(e.target.value);
    // 输入内容变化时同步更新标签状态
    setIsLabelActive(e.target.value.length > 0);
  };

  return (
    <div className="relative mb-5">
      <input
        type={type}
        name={name}
        value={value}
        onChange={handleChange}
        onFocus={handleFocus}
        onBlur={handleBlur}
        className={`w-full px-4 py-2 border border-gray-300 rounded-lg outline-none transition-all duration-300 ${
          isLabelActive ? 'pt-6' : 'pt-2'
        }`}
      />
      <label
        className={`absolute left-4 top-2 text-gray-500 transition-all duration-300 ${
          isLabelActive ? 'top-0 text-xs font-medium' : 'text-base'
        }`}
      >
        {label}
      </label>
    </div>
  );
}

逻辑说明:

  • 用firstMount标记组件是否为首次挂载,后续页面返回时的重新挂载会跳过聚焦触发的动画逻辑
  • 标签状态由输入内容和首次交互共同控制,避免单纯依赖focus事件导致的重复动画

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 09:37:40