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

React页面提示消息超时引发无限循环的修复方案求助

问题

我正在开发一款需展示用户创建成功或失败提示消息的应用,采用Deno的Fresh框架(基于Preact,可视为精简版React)。当前页面中,用户创建操作后的提示消息无法在3秒后自动消失,且浏览器控制台出现无限循环现象。

我的网页代码如下:

// import { useState } from "preact/hooks";
import { useState, useEffect } from "preact/hooks";
import { Handlers, PageProps } from "$fresh/server.ts";
import UserDb from "../database.ts";

interface CreatorProps {
  email: string,
  key: string
}

export default function Creator(props: CreatorProps) {
  const [alertPointer, setAlertPointer] = useState(0);
  const [alertInst, setAlertInst] = useState("");

  useEffect(() => {
    function createAlert() {
      if (alertPointer === 0) {
        null;
      } else if (alertPointer === 1) {
        setAlertInst(
          <div class="bg-blue-100 border-t border-b border-blue-500 text-blue-700 px-4 py-3" role="alert">
            <p class="font-bold">User Creation Succeeded</p>
            <p class="text-sm">A new user was added to the database</p>
          </div>
        );
      } else {
        setAlertInst(
          <div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative" role="alert">
            <strong class="font-bold">User Creation Failed</strong>
            <span class="block sm:inline">Are you sure you entered in a valid email and key?</span>
            <span class="absolute top-0 bottom-0 right-0 px-4 py-3">
              <svg class="fill-current h-6 w-6 text-red-500" role="button" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><title>Close</title><path d="M14.348 14.849a1.2 1.2 0 0 1-1.697 0L10 11.819l-2.651 3.029a1.2 1.2 0 1 1-1.697-1.697l2.758-3.15-2.759-3.152a1.2 1.2 0 1 1 1.697-1.697L10 8.183l2.651-3.031a1.2 1.2 0 1 1 1.697 1.697l-2.758 3.152 2.758 3.15a1.2 1.2 0 0 1 0 1.698z"/></svg>
            </span>
          </div>
        )
      }
    }
    const timeId = setTimeout(() => {
      // After 3 seconds set the show value to false
      setAlertPointer(0);
      setAlertInst("");
    }, 3000)

    function removeItem() {
      console.log("rm item start");
      clearTimeout(timeId)
      console.log("rm item finished");
      console.log(timeId);
      console.log(alertPointer);
      console.log(alertInst);
    }
    createAlert();
    removeItem();
  }, [alertPointer, alertInst]);
  
  async function handleSubmit(event) {
    event.preventDefault();
    const emailInput = event.target.email;
    const ageInput = event.target.key;
    const resp = await createNewUser(emailInput.value, ageInput.value);
    return resp
  };

  async function createNewUser(email, key) {
    const rawPosts = await fetch('http://localhost:8000/api/createUser', {
      "method": "POST",
      "headers": {
        "content-type": "application/json"
      },
      "body": JSON.stringify({
        email: email,
        key: key,
      })
    });
    const respns = JSON.parse(await rawPosts.json());
    if (respns.isUserCreated) {
      setAlertPointer(1);
    } else {
      setAlertPointer(2);
    }
  }

  return (
    <div>
      {alertInst}
      <h1 class="text rounded-lg p-4 my-8"> Search </h1>
      <form method="post" onSubmit={async (e) => handleSubmit(e)}>
        <input class="center rounded-lg p-4 my-8" id="email" name="email" />
        <input class="center rounded-lg p-4 my-8" id="key" name="key" />
        <br />
        <button
          class="px-5 py-2.5 text-sm font-medium bg-blue-600 rounded-md shadow disabled:(bg-gray-800 border border-blue-600 opacity-50 cursor-not-allowed)"
          type="submit">Submit
        </button>
      </form>
      <br />
      {/* <ul>
        {results.map((name) => <li key={name}>{name}</li>)}
      </ul> */}
    </div>
  );
};

创建用户成功时页面会显示蓝色的成功提示框,但提示消息从未消失,控制台会出现无限循环打印日志的现象。


修复方案

问题根源

  1. 无限循环触发:useEffect依赖数组包含alertInst,但内部又调用setAlertInst修改该状态,导致useEffect反复执行形成循环;同时每次执行时立刻调用removeItem清除定时器,导致定时器根本无法触发,提示框无法自动消失。
  2. 冗余状态维护:alertPointer和alertInst两个状态重复,完全可以通过alertPointer的值直接渲染提示内容,不需要额外维护alertInst。

具体修复步骤

  1. 移除alertInst状态:直接在渲染阶段根据alertPointer的值生成对应的提示框,避免状态同步问题。
  2. 调整useEffect逻辑:只监听alertPointer变化,当状态不为0时启动定时器;将定时器清理逻辑放在useEffect的返回函数中,这是Preact/React清理副作用的标准方式。
  3. 修复JSON解析错误:rawPosts.json()已经返回解析后的对象,不需要再调用JSON.parse。

修复后的代码

import { useState, useEffect } from "preact/hooks";
import { Handlers, PageProps } from "$fresh/server.ts";
import UserDb from "../database.ts";

interface CreatorProps {
  email: string,
  key: string
}

export default function Creator(props: CreatorProps) {
  const [alertPointer, setAlertPointer] = useState(0);

  useEffect(() => {
    let timerId: number | undefined;
    // 仅当提示状态激活时启动定时器
    if (alertPointer !== 0) {
      timerId = setTimeout(() => {
        setAlertPointer(0);
      }, 3000);
    }
    // 组件卸载或状态变化时清除定时器
    return () => {
      if (timerId) clearTimeout(timerId);
    };
  }, [alertPointer]);
  
  async function handleSubmit(event) {
    event.preventDefault();
    const emailInput = event.target.email;
    const keyInput = event.target.key;
    await createNewUser(emailInput.value, keyInput.value);
  };

  async function createNewUser(email, key) {
    const response = await fetch('http://localhost:8000/api/createUser', {
      method: "POST",
      headers: {
        "content-type": "application/json"
      },
      body: JSON.stringify({ email, key })
    });
    const respns = await response.json();
    setAlertPointer(respns.isUserCreated ? 1 : 2);
  }

  // 根据状态渲染对应提示框
  const renderAlert = () => {
    switch (alertPointer) {
      case 1:
        return (
          <div class="bg-blue-100 border-t border-b border-blue-500 text-blue-700 px-4 py-3" role="alert">
            <p class="font-bold">用户创建成功</p>
            <p class="text-sm">新用户已添加到数据库</p>
          </div>
        );
      case 2:
        return (
          <div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative" role="alert">
            <strong class="font-bold">用户创建失败</strong>
            <span class="block sm:inline">请确认输入的邮箱和密钥是否有效?</span>
            <span 
              class="absolute top-0 bottom-0 right-0 px-4 py-3 cursor-pointer"
              onClick={() => setAlertPointer(0)}
            >
              <svg class="fill-current h-6 w-6 text-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
                <title>关闭</title>
                <path d="M14.348 14.849a1.2 1.2 0 0 1-1.697 0L10 11.819l-2.651 3.029a1.2 1.2 0 1 1-1.697-1.697l2.758-3.15-2.759-3.152a1.2 1.2 0 1 1 1.697-1.697L10 8.183l2.651-3.031a1.2 1.2 0 1 1 1.697 1.697l-2.758 3.152 2.758 3.15a1.2 1.2 0 0 1 0 1.698z"/>
              </svg>
            </span>
          </div>
        );
      default:
        return null;
    }
  };

  return (
    <div>
      {renderAlert()}
      <h1 class="text rounded-lg p-4 my-8"> 用户创建 </h1>
      <form method="post" onSubmit={handleSubmit}>
        <input class="center rounded-lg p-4 my-8" id="email" name="email" placeholder="输入邮箱" />
        <input class="center rounded-lg p-4 my-8" id="key" name="key" placeholder="输入密钥" />
        <br />
        <button
          class="px-5 py-2.5 text-sm font-medium bg-blue-600 rounded-md shadow disabled:(bg-gray-800 border border-blue-600 opacity-50 cursor-not-allowed)"
          type="submit">提交
        </button>
      </form>
      <br />
    </div>
  );
};

额外优化

  • 给失败提示框的关闭按钮添加点击事件,支持手动关闭
  • 输入框添加占位符,优化用户体验
  • 提示文本改为中文,适配国内用户习惯

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 22:25:33