Home > Net >  Timeout alert message causes infinite loop in React page
Timeout alert message causes infinite loop in React page

Time:09-20

I am making an app the is supposed to flash a message indicating user creation success or failure. I am using deno's fresh framework which employs "preact". Preact is essentially a minimal version of react so just assume that what I'm doing would work the same in a React page.

My webpage code looks like this:

// 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  role="alert">
            <p >User Creation Succeeded</p>
            <p >A new user was added to the database</p>
          </div>
        );
      } else {
        setAlertInst(
          <div  role="alert">
            <strong >User Creation Failed</strong>
            <span >Are you sure you entered in a valid email and key?</span>
            <span >
              <svg  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 > Search </h1>
      <form method="post" onSubmit={async (e) => handleSubmit(e)}>
        <input  id="email" name="email" />
        <input  id="key" name="key" />
        <br />
        <button
          
          type="submit">Submit
        </button>
      </form>
      <br />
      {/* <ul>
        {results.map((name) => <li key={name}>{name}</li>)}
      </ul> */}
    </div>
  );
};

When I create a user that succeeds the page looks like this:
enter image description here

But the message never disappears and the web console looks like this:
enter image description here

How do I fix this? How do I make the alert indicating success or failure vanish after 3 seconds?

CodePudding user response:

The issue I see is that once the alertPointer state is updated to a non-zero value it's never set back to 0. The removeItem is called and clears the running timeout that was just instantiated to reset the alertPointer state back to 0. alertPointer equals 1 and state updates are continued to be enqueued.

useEffect(() => {
  function createAlert() {
    if (alertPointer === 0) {
      null;
    } else if (alertPointer === 1) {
      setAlertInst(....);
    } else {
      setAlertInst(....)
    }
  }

  const timeId = setTimeout(() => { // (1) <-- timeout instantiated
    // After 3 seconds set the show value to false
    setAlertPointer(0);
    setAlertInst("");
  }, 3000)

  function removeItem() {
    console.log("rm item start");
    clearTimeout(timeId); // (3) <-- cleared
    console.log("rm item finished");
    console.log(timeId);
    console.log(alertPointer);
    console.log(alertInst);
  }

  createAlert();
  removeItem(); // (2) <-- timeout cleared
}, [alertPointer, alertInst]);

You should store the timer id in a React ref (via the useRef hook) and return useEffect cleanup function in the case the component unmounts prior to the timeout expiring. This allows the normal operation of the timeout while the component is mounted.

Example:

const timerIdRef = useRef();

// Clear any running timeouts when the component unmounts
useEffect(() => {
  return () => {
    clearTimeout(timerIdRef.current);
  };
}, []);

useEffect(() => {
  function createAlert() {
    if (alertPointer === 0) {
      null;
    } else if (alertPointer === 1) {
      setAlertInst(....);
    } else {
      setAlertInst(....)
    }
  }

  timerIdRef.current = setTimeout(() => {
    // After 3 seconds set the show value to false
    setAlertPointer(0);
    setAlertInst("");
  }, 3000)

  createAlert();
}, [alertPointer, alertInst]);

CodePudding user response:

try to add dependencies in use effect

  • Related